Skip to content

refactor(evaluator)!: rename env_overrides to hydra_params, add env_vars - #1314

Merged
SandyChapman merged 1 commit into
mainfrom
aalgo-485-gym-config-naming/schapman
Aug 17, 2026
Merged

refactor(evaluator)!: rename env_overrides to hydra_params, add env_vars#1314
SandyChapman merged 1 commit into
mainfrom
aalgo-485-gym-config-naming/schapman

Conversation

@SandyChapman

Copy link
Copy Markdown
Contributor

Summary

env_overrides reads as "environment variables" to everyone who meets it, and it isn't — the "env" is Gym's environment (the resources-server), and the values become Hydra config overrides ({'a': {'b': 1}}++a.b=1). Renamed to hydra_params, which says what it does.

That frees the name for the thing people assumed it was. Some Gym environments are configurable only through the OS environment: wmt_translation reads WMT_TRANSLATION_COMET_PY_CACHE for its model-cache root and otherwise defaults to /opt/Gym/.cache/comet-python, a path that exists only inside NVIDIA's container image — so anywhere else it dies with PermissionError: /opt/Gym before the GPU requirement is even reached. Until now the only way to set that was to export it before invoking the runner, which turns a property of the environment into a property of whoever launched the run. A job spec executed elsewhere has no ambient environment to inherit from at all. New env_vars field carries it in the config instead.

Related Issue

Tracked in Linear as AALGO-485. No GitHub issue.

Stacked on #1295 — both change the same regions of gym_runtime.py, so basing on main would guarantee a conflict. Review #1295 first; once it merges, this diff collapses to just this commit.

Changes

  • env_overrideshydra_params on GymRuntimeConfig and the Gym runner-target job spec, plus _redact_env_overrides_redact_hydra_params (leaving the old name on the helper would defeat the point). Descriptions now say explicitly that the two fields are different things.
  • New env_vars: dict[str, str] on both, merged into the environment the gym CLI is invoked with.
  • _gym_invocation_env(config) extracted so the precedence is assertable without starting Ray: process environment → RAY_ENABLE_UV_RUN_RUNTIME_ENV=0env_vars.
  • env_vars redacted in RunnerInfo.config on the same rules as hydra_params.
  • Plugin spec regeneratedmake refresh-openapi, verified idempotent.

Design calls

  • env_vars outranks the Ray default. That setting exists to make Gym work, not as an invariant; someone debugging Ray's uv hook needs a way to restore it. Explicit config beats a built-in default.
  • Redaction reuses hydra_params' markers rather than a new list. Verified against real names: OPENAI_API_KEY, HF_TOKEN, AWS_SECRET_ACCESS_KEY and DB_PASSWORD all redact; WMT_TRANSLATION_COMET_PY_CACHE and HTTPS_PROXY stay verbatim. env_vars needs this more than hydra_params does — an environment variable is the conventional way to hand a process an API key, so a caller doing the obvious thing would otherwise write one into the run bundle.
  • Clean rename, no deprecated alias. The field landed in main one day ago (feat(evaluator): add GymRunnerTarget plugin wiring and Studio rendering #1257), nothing in web/ references it, and it is absent from the generated Python SDK. Adoption is as close to zero as it will ever be; an alias now would be permanent cost for a day-old field.
  • Left alone: the unrelated env_overrides in services/rl and services/core/auth. Different concepts that happen to share a name — and the auth one is genuinely Mapping[str, str] of OS environment variables, which is its own small argument that the old name misleads.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with documentation updates
  • Documentation only
  • Contributor tooling or automation
  • CI, build, or test infrastructure

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Documentation updated for user-visible behavior
  • Documentation not applicable — justification: the fields are documented in their own Field(description=...), which is what surfaces in the OpenAPI spec and is regenerated here. examples/gym/README.md is updated for the rename. No Fern page documents the Gym runner target.

Verification

  • Pull request title follows the repository's Conventional Commit format
  • Every commit includes an appropriate Signed-off-by: trailer
  • uv run pre-commit run -a passes, or any blocked checks are identified below
  • Targeted tests pass, or tests are marked not applicable above
  • No secrets, API keys, or credentials are included

Targeted validation:

Command Result
pytest packages/nemo_evaluator_sdk/tests -q 1586 passed, 10 skipped
pytest plugins/nemo-evaluator/tests --ignore=…/integration -q 813 passed
uv run ruff check All checks passed
uv run ruff format --check 350 files already formatted
tools/lint/lint-python-types.sh exit 0
make refresh-openapi regenerated; byte-identical on a second run
make vendor mirror in sync; byte-identical on a second run

Run on Python 3.12, matching make toolchain-versions. See the note below on why that matters.

On make update-sdk — run, and deliberately reverted

I ran it. The Stainless half succeeded but produced nothing for this change, and I reverted what it did produce:

  • The root openapi/openapi.yaml — the spec Stainless consumes — is unchanged by this work, and contains zero references to the Gym runner target (hydra_params, env_overrides, resources_server). That target lives only in plugins/nemo-evaluator/openapi/openapi.yaml, which is not Stainless's input. The generated Python SDK never exposed these fields, so renaming them cannot affect it. make refresh-openapi alone was the whole job.
  • What it did change was unrelated: access_keys.py, its types __init__, and its tests — the committed SDK no longer matches what Stainless generates from the current spec. The regenerated list() drops the page / page_size docstring params and rewrites the query transform, which looks like a regression rather than cosmetic drift. Reverted here; it deserves its own investigation rather than riding along invisibly in a rename.
  • make update-web-sdk failed outright: tsx: command not found, node_modules missing, because this worktree has never run pnpm install in web/. Not run, since per the above there was nothing of this change to generate. Worth knowing that make update-sdk piped through tail reports exit 0 even when this step fails.

Blocked checks (host toolchain, not this change)

Hook Failure Why it is not this change
uv-lock requires uv 0.9.14, host has 0.9.30 diff touches no pyproject.toml/uv.lock; uv-lock-check, which catches real drift, passes
studio-lint-staged mise ERROR No version is set for shim: pnpm diff touches no web/ files
Helm Docs helm-docs container unavailable locally diff touches no Helm chart

Unrelated finding, filed separately

test_harbor_runtime.py::test_symlink_loop_degrades_the_stamp_instead_of_killing_the_run fails on Python 3.13 and passes on 3.12. Its premise asserts Path.resolve() raises RuntimeError for a symlink loop, which is 3.12 behaviour; on 3.13 it raises nothing. It surfaced because a fresh worktree's venv resolved 3.13.12 — requires-python = ">=3.12,<3.15" permits it even though make toolchain-versions reports 3.12. Nothing to do with this change, but it means the package claims support for a version at least one test does not hold on.

@github-actions github-actions Bot added breaking breaking change (!-marked title) refactor labels Aug 14, 2026
@SandyChapman
SandyChapman marked this pull request as ready for review August 14, 2026 13:49
@SandyChapman
SandyChapman requested review from a team as code owners August 14, 2026 13:49
Base automatically changed from aalgo-485-gym-error-legibility/schapman to main August 17, 2026 13:25
`env_overrides` read as "environment variables" to everyone who met it. It is
not: the "env" is Gym's *environment* (the resources-server), and the values
become Hydra config overrides — `{'a': {'b': 1}}` flattens to `++a.b=1`. The
name is now `hydra_params`, which says what it does.

That frees `env` to mean what people assumed, and the gap it leaves is real.
Some Gym environments are configurable only through the OS environment:
`wmt_translation` reads `WMT_TRANSLATION_COMET_PY_CACHE` for its model-cache
root and otherwise defaults to `/opt/Gym/.cache/comet-python`, a path that
exists only inside NVIDIA's container image, so on any other machine it fails
with `PermissionError: /opt/Gym` before the GPU requirement is even reached.

Until now the only way to set that was to export it before invoking the
runner, which makes a property of the *environment* into a property of
whoever happened to launch the run — and a job spec executed elsewhere has no
ambient environment to inherit from at all. The new `env_vars` field carries
it in the config instead.

Precedence is this process's environment, then the Ray uv-hook default, then
`env_vars`. Explicit config wins over both, including over the Ray setting:
that default exists to make Gym work rather than as an invariant, and someone
debugging that hook needs a way to put it back. `_gym_invocation_env` exists
so this is assertable without starting Ray.

`env_vars` is redacted in `RunnerInfo.config` on the same rules as
`hydra_params`, and needs it more: an environment variable is the conventional
way to hand a process an API key, so a caller doing the obvious thing would
otherwise write one into the run bundle. Verified the existing markers catch
`OPENAI_API_KEY`, `HF_TOKEN`, `AWS_SECRET_ACCESS_KEY` and `DB_PASSWORD` while
leaving `WMT_TRANSLATION_COMET_PY_CACHE` and `HTTPS_PROXY` verbatim.

BREAKING CHANGE: `env_overrides` is renamed to `hydra_params` on both
`GymRuntimeConfig` and the Gym runner-target job spec, so it changes the REST
contract in plugins/nemo-evaluator/openapi/openapi.yaml. The field landed in
main one day ago (#1257) and has no known callers.

Signed-off-by: Sandy Chapman <schapman@nvidia.com>

Rebased onto main after #1295 split `gym_runtime.py` into the `gym/` package.
Re-applied on the new layout rather than resolving a delete/modify conflict
against a file that no longer exists. One placement changed as a result:
`_gym_invocation_env` now lives in `gym/process.py` rather than beside the
runner. The environment a CLI is invoked with is part of how it is run, which
is what that module is for, and it keeps the runtime module orchestration-only.
It is `process.py`'s first dependency on `config.py`, which stays acyclic.

Signed-off-by: Sandy Chapman <schapman@nvidia.com>
@SandyChapman
SandyChapman force-pushed the aalgo-485-gym-config-naming/schapman branch from 43b6e9e to bc6bb6e Compare August 17, 2026 13:34
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

An error occurred during the review process. Please try again later.


Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 33501/42238 79.3% 64.3%
Integration Tests 19542/40037 48.8% 21.0%

@SandyChapman
SandyChapman added this pull request to the merge queue Aug 17, 2026
Merged via the queue into main with commit f231a4b Aug 17, 2026
59 checks passed
@SandyChapman
SandyChapman deleted the aalgo-485-gym-config-naming/schapman branch August 17, 2026 17:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking breaking change (!-marked title) refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants