Skip to content

Support graph-spec YAML environments in typed YAML experiment configs#943

Open
alexmillane wants to merge 4 commits into
mainfrom
alexmillane/feature/yaml-env-experiment-config
Open

Support graph-spec YAML environments in typed YAML experiment configs#943
alexmillane wants to merge 4 commits into
mainfrom
alexmillane/feature/yaml-env-experiment-config

Conversation

@alexmillane

@alexmillane alexmillane commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Support graph-YAML environments in typed YAML experiment configs

Detailed description

  • The typed YAML experiment frontend resolved environment.type only against registered environment names, so graph-spec YAML environments (e.g. isaaclab_arena_environments/robolab/tasks/*.yaml) were reachable only through the legacy JSON format or the CLI.
  • An environment.type ending in .yaml/.yml now builds the same temporary LegacyGraphEnvironmentCfg compatibility config the JSON frontend produces, via a graph_environment_cfg_factory injected from the evaluation layer next to the other typed-config-migration shims.
  • Graph runs execute with the Run's typed environment_builder config (not one re-derived from CLI tokens), so post-load Hydra overrides such as experiment_cfg.runs.X.environment_builder.num_envs=4 on an OSMO submission take effect; the pre-launch camera guard also recognizes graph runs.
  • osmo/submit_arena_experiment.py now works with graph-run experiments: the compatibility config records its graph-spec path and source values, and serialize_arena_experiment_to_yaml emits them, so the embedded per-Run handoff round-trips through the runner loader. The GR00T remote policy config's Literal annotation was also relaxed so it composes in typed YAML experiments.
  • Verified in the container: loader/serializer/OSMO unit tests; end-to-end experiment_runner.py on a robolab graph run with a num_envs override applied; osmo.submit_arena_experiment --policy_server pi0 osmo.dry_run=true renders a 4-run robolab workflow with embedded graph experiments.

The typed YAML experiment frontend previously resolved environment.type
only against registered environment names; graph-spec YAML environments
(e.g. the robolab tasks) were reachable only through the legacy JSON
format or the CLI. An environment.type ending in .yaml/.yml now routes
through the same LegacyGraphEnvironmentCfg compatibility path the JSON
frontend uses, and the camera pre-launch guard recognizes such runs.

Signed-off-by: Alex Millane <amillane@nvidia.com>
@arena-review-bot

Copy link
Copy Markdown
Contributor

🤖 Isaac Lab-Arena Review Bot

Summary

This PR lets typed YAML experiment configs select graph-spec YAML environments (environment.type ending in .yaml/.yml) by building the same temporary LegacyGraphEnvironmentCfg compatibility config the JSON frontend produces. The design is clean: the generic Hydra loader stays dependency-free via an injected graph_environment_cfg_factory, the actual conversion lives in the evaluation layer next to the other [typed-config-migration] shims, and everything is TODO-marked for deletion. The camera pre-launch guard is correctly extended so a graph-YAML run with --enable_cameras in its tokens still fails fast when the flag is missing.

Findings

No blocking issues found. A few things I verified and liked:

  • The _run_environment_requires_cameras change is a real fix, not just a refactor: ArenaEnvironmentCfg has no enable_cameras field, so the old inline getattr(..., "enable_cameras", False) would have returned False for graph-YAML runs and silently skipped the guard.
  • num_envs flows consistently to both the rendered CLI tokens and the typed environment_builder, mirroring the JSON path exactly.
  • The no-factory case fails loud with a clear assertion rather than a confusing downstream error.

Test Coverage

Well covered. The new unit tests assert the exact arena_env_args token order, the camera-guard behavior for a LegacyGraphEnvironmentCfg, the injected-factory contract, and the no-factory assertion. These are Phase-1 (no cameras / no subprocess) unit tests that never start the sim, so the inner/outer run_simulation_app_function pattern correctly does not apply.

Verdict

Ship it

@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extends the typed YAML experiment frontend so that an environment.type value ending in .yaml/.yml is routed through the existing LegacyGraphEnvironmentCfg argparse compatibility path, instead of failing with an unknown-selector error. The same legacy_environment_args_to_cli_args serialisation used by the JSON frontend is reused, and the pre-launch camera guard is widened to cover the new run type.

  • Adds _graph_spec_yaml_selector in typed_experiment_loader.py and an injectable graph_environment_cfg_factory parameter that keeps the shim isolated from the generic Hydra loader.
  • Implements _graph_environment_cfg_from_yaml_values in arena_experiment_config_loader.py to merge environment_builder and remaining environment values into CLI tokens, mirroring the JSON path.
  • Extends _run_environment_requires_cameras in experiment_runner.py to detect --enable_cameras in arena_env_args for LegacyGraphEnvironmentCfg runs.

Confidence Score: 4/5

Safe to merge; the changes are a well-scoped compatibility shim with good test coverage, touching only the experiment loading and camera-guard paths.

The implementation is clean and consistent with existing patterns. The only issue found is a minor dict construction order in _graph_environment_cfg_from_yaml_values where "environment" is placed first in the literal, meaning a hypothetically colliding key in the spread dicts could silently overwrite the graph-spec YAML path. Tests cover the factory contract, token ordering, camera detection, and the no-factory assertion.

isaaclab_arena/evaluation/arena_experiment_config_loader.py — the _graph_environment_cfg_from_yaml_values dict literal ordering

Important Files Changed

Filename Overview
isaaclab_arena/hydra/typed_experiment_loader.py Adds graph-spec YAML detection via _graph_spec_yaml_selector and routes matching runs through the injected graph_environment_cfg_factory; the factory is threaded through the call chain cleanly.
isaaclab_arena/evaluation/arena_experiment_config_loader.py Adds _graph_environment_cfg_from_yaml_values factory merging environment_builder_values then environment_values into CLI args; the "environment" key is placed first and could theoretically be shadowed by a colliding spread key.
isaaclab_arena/evaluation/experiment_runner.py Extends camera-guard to cover LegacyGraphEnvironmentCfg by checking "--enable_cameras" token presence; logic is correct and the helper is well-isolated.
isaaclab_arena/tests/test_arena_experiment_config_loader.py Adds two focused tests: full integration test for the new graph-YAML loading path (token ordering, builder values, device override) and a camera-guard assertion test for the new run type.
isaaclab_arena/tests/test_experiment_hydra.py Adds unit tests for the factory contract (values received, environment equality), token rendering, and the no-factory assertion; these tests do not require a real environment registration.

Sequence Diagram

sequenceDiagram
    participant User
    participant Loader as arena_experiment_config_loader
    participant HydraLoader as typed_experiment_loader
    participant CliArgs as legacy_environment_cli_args
    participant Runner as experiment_runner

    User->>Loader: load_arena_experiment_from_config_file(path, device)
    Loader->>HydraLoader: load_arena_experiment_from_yaml(path, env_cfg_types, policy_resolver, graph_env_factory)

    loop for each Run
        HydraLoader->>HydraLoader: _graph_spec_yaml_selector(environment_values)
        alt type ends with .yaml/.yml
            HydraLoader->>Loader: graph_environment_cfg_factory(graph_spec_yaml, env_values, builder_values)
            Loader->>CliArgs: "legacy_environment_args_to_cli_args({environment, ...builder_values, ...env_values})"
            CliArgs-->>Loader: [--num_envs, 2, --enable_cameras, --env_graph_spec_yaml, path, ...]
            Loader-->>HydraLoader: "LegacyGraphEnvironmentCfg(arena_env_args=[...])"
        else registered selector name
            HydraLoader->>HydraLoader: _compose_typed_config_from_yaml_selector(...)
        end
        HydraLoader->>HydraLoader: Compose ArenaRunCfg via Hydra (remaining_values incl. environment_builder)
    end

    HydraLoader-->>Loader: "ArenaExperimentCfg(runs={...})"
    Loader->>Loader: Apply device to each run.environment_builder

    User->>Runner: main() / _assert_camera_support_enabled(experiment_cfg, enable_cameras)
    loop for each Run
        Runner->>Runner: _run_environment_requires_cameras(run_cfg)
        alt isinstance LegacyGraphEnvironmentCfg
            Runner->>Runner: --enable_cameras in arena_env_args
        else typed environment
            Runner->>Runner: getattr(environment, enable_cameras, False)
        end
    end
Loading

Reviews (1): Last reviewed commit: "Support graph-spec YAML environments in ..." | Re-trigger Greptile

Comment on lines +101 to +105
arena_env_args: dict[str, Any] = {
"environment": env_graph_spec_yaml,
**environment_builder_values,
**environment_values,
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The "environment" key is set first in the literal, so a key named "environment" in either environment_builder_values or environment_values (e.g. a YAML field literally called environment nested inside the environment: section) would silently overwrite env_graph_spec_yaml. Placing it last guarantees it always wins.

Suggested change
arena_env_args: dict[str, Any] = {
"environment": env_graph_spec_yaml,
**environment_builder_values,
**environment_values,
}
arena_env_args: dict[str, Any] = {
**environment_builder_values,
**environment_values,
"environment": env_graph_spec_yaml,
}

language_instruction is not a flag on the graph-environment parser (it is
injected from the typed builder config after parsing, as is device), so
rendering it as a token made argparse swallow the value as the
example-environment positional.

Signed-off-by: Alex Millane <amillane@nvidia.com>
Literal annotations are rejected by OmegaConf structured configs, which
blocked the GR00T remote policy from typed YAML Experiments; the policy
constructor already validates the value at runtime.

Signed-off-by: Alex Millane <amillane@nvidia.com>
osmo/submit_arena_experiment.py embeds the effective Experiment by
re-serializing it, which failed for graph-YAML environments because the
serializer only resolves registry-registered configs. The compatibility
config now records its graph-spec path and source environment values,
and the serializer emits them as the environment section.

Graph runs also now execute with the Run's typed environment_builder
config instead of one re-derived from CLI tokens, so post-load Hydra
overrides (e.g. environment_builder.num_envs on an OSMO submission)
take effect; tokens carry only environment values.

Signed-off-by: Alex Millane <amillane@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant