feat: run v0 environments on the eval CLI (legacy bridge) - #1598
Conversation
Backwards-compat: `uv run eval --legacy.id <v0-env-id> [--legacy.args '{...}']` evaluates
a classic `verifiers.load_environment` env through the v1 eval, bridged to v1 Traces. All
v0 glue lives in verifiers/v1/legacy.py (`run_legacy_eval`, reusing the existing
`rollout_output_to_trace`); the env runs in-process via `env.run_rollout` (no env server /
runtime / interception). Minimal v1 wiring: a `LegacyConfig` (id + args) on EvalConfig and
a one-branch dispatch in the eval CLI; the --rich dashboard (v1 Rollout state) is off for
legacy runs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… prime-rl)
Put the v0/legacy selector on the base `EnvConfig` — `id` + `args` (forwarded to
`load_environment`) plus `is_legacy` / `env_id` properties — matching prime-rl's shape, so
`EvalConfig` and `EnvServerConfig` inherit it (and prime-rl can drop its duplicates). The
eval CLI now branches on `config.is_legacy` and takes the v0 env via `--id <env> [--args
'{...}']` instead of the nested `--legacy.id`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`extra_env_kwargs` (on EnvConfig, applied to the loaded v0 env via `env.set_kwargs`) carries post-load knobs like `max_total_completion_tokens` / `max_seq_len` / `timeout_seconds`, distinct from `args` (construction kwargs) — mirroring prime-rl main's `extra_env_kwargs` (missing from this branch). Wired into both legacy entry points (`run_legacy_eval` and `LegacyEnvServer`). The v0/legacy fields on EnvConfig (`id`/`args`/`extra_env_kwargs` + `is_legacy`/`env_id`) are now grouped under an explicit legacy separator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve the legacy.py conflict by taking #1597's on-demand hub install (`ensure_installed` / `env_name`) in `LegacyEnvServer` and keeping `extra_env_kwargs` (applied via `env.set_kwargs`). Wire the same hub install into `run_legacy_eval` (so `--id org/name[@Version]` installs on demand) and type `EnvConfig.id` as `EnvId`, matching the taskset/harness id fields. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit adbcf95. Configure here.
|
|
||
| # `num_rollouts` rollouts per selected task, all bounded by the one semaphore. | ||
| coros = [run_one(i) for i in idxs for _ in range(config.num_rollouts)] | ||
| return list(await asyncio.gather(*coros)) |
There was a problem hiding this comment.
Skips v0 group scoring
Medium Severity
When num_rollouts is greater than one, run_legacy_eval schedules separate env.run_rollout calls per rollout. Classic v0 evaluation uses env.run_group so rubric.score_group runs; isolated rollouts only get per-rollout scoring, so group-reward environments return incorrect rewards and metrics.
Reviewed by Cursor Bugbot for commit adbcf95. Configure here.
| if config.shuffle: | ||
| random.Random(0).shuffle(idxs) # fixed seed: same sample every run | ||
| if config.num_tasks is not None: | ||
| idxs = idxs[: config.num_tasks] |
There was a problem hiding this comment.
Uses train split not eval
Medium Severity
Legacy eval builds task indices from env.get_dataset(), which uses the training dataset. Standard v0 evaluate() uses get_eval_dataset() (with train fallback only when no eval split exists), so environments with a separate eval split can be scored on the wrong examples.
Reviewed by Cursor Bugbot for commit adbcf95. Configure here.
| if config.is_legacy: # v0 backwards-compat: run the classic env, bridged to Traces | ||
| from verifiers.v1.legacy import run_legacy_eval | ||
|
|
||
| traces = asyncio.run(run_legacy_eval(config)) |
There was a problem hiding this comment.
Missing evaluation documentation
Medium Severity
This PR adds a user-facing legacy uv run eval --id path, EnvConfig legacy fields, and extra_env_kwargs wiring, but the diff does not update the evaluation docs, reference material, or affected skills to describe the new CLI contract.
Triggered by project rule: BugBot Instructions
Reviewed by Cursor Bugbot for commit adbcf95. Configure here.
ApprovabilityVerdict: Needs human review This PR introduces new feature functionality (legacy v0 environment bridge) and has two unresolved medium-severity bug reports: using training dataset instead of eval dataset, and skipping group scoring for multi-rollout evaluations. These correctness concerns require human review. You can customize Macroscope's approvability policy. Learn more. |
…lect-ai#1598) * feat(v1): run v0 environments on the `eval` CLI (--legacy.id) Backwards-compat: `uv run eval --legacy.id <v0-env-id> [--legacy.args '{...}']` evaluates a classic `verifiers.load_environment` env through the v1 eval, bridged to v1 Traces. All v0 glue lives in verifiers/v1/legacy.py (`run_legacy_eval`, reusing the existing `rollout_output_to_trace`); the env runs in-process via `env.run_rollout` (no env server / runtime / interception). Minimal v1 wiring: a `LegacyConfig` (id + args) on EvalConfig and a one-branch dispatch in the eval CLI; the --rich dashboard (v1 Rollout state) is off for legacy runs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(v1): move the legacy env selector onto EnvConfig (align with prime-rl) Put the v0/legacy selector on the base `EnvConfig` — `id` + `args` (forwarded to `load_environment`) plus `is_legacy` / `env_id` properties — matching prime-rl's shape, so `EvalConfig` and `EnvServerConfig` inherit it (and prime-rl can drop its duplicates). The eval CLI now branches on `config.is_legacy` and takes the v0 env via `--id <env> [--args '{...}']` instead of the nested `--legacy.id`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(v1): add extra_env_kwargs to the legacy env config `extra_env_kwargs` (on EnvConfig, applied to the loaded v0 env via `env.set_kwargs`) carries post-load knobs like `max_total_completion_tokens` / `max_seq_len` / `timeout_seconds`, distinct from `args` (construction kwargs) — mirroring prime-rl main's `extra_env_kwargs` (missing from this branch). Wired into both legacy entry points (`run_legacy_eval` and `LegacyEnvServer`). The v0/legacy fields on EnvConfig (`id`/`args`/`extra_env_kwargs` + `is_legacy`/`env_id`) are now grouped under an explicit legacy separator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(v1): mark --id as legacy in the eval usage line Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>


Summary
uv run evalentrypoint can now evaluate a classic v0 environment via--id <env-id>, bridged to v1Traces — same mechanism the orchestrator uses (rollout_output_to_trace), run in-process for eval (no env server).verifiers/v1/legacy.py(run_legacy_eval, reusingrollout_output_to_trace): loads the v0 env, runsnum_rolloutsper task with bounded concurrency viaenv.run_rollout, maps eachRolloutOutput→Trace, writes the sameresults.jsonl/config.tomla native run does.vf.EnvConfig, grouped under an explicit legacy (v0) separator, matching prime-rl's shape soEvalConfig/EnvServerConfiginherit it:id— v0 env id (load_environment(id, **args))args— construction kwargsextra_env_kwargs— post-load knobs applied viaenv.set_kwargs(**…)(e.g.max_total_completion_tokens,max_seq_len,timeout_seconds)is_legacy(id set, no taskset) /env_idpropertiesconfig.is_legacy;--rich(v1Rolloutstate) is auto-off for legacy runs.Alignment with prime-rl
Mirrors prime-rl's legacy wiring: prime-rl's
EnvConfig(vf.EnvConfig)exposesid/args/extra_env_kwargs/is_legacy/env_id, and its env server doesif env.is_legacy: LegacyEnvServer(env_id, env_args, extra_env_kwargs).extra_env_kwargsis on prime-rl main but missing fromfeat/nano-as-v1— adding it to the basevf.EnvConfigfills that gap and lets prime-rl drop its duplicates and inherit. Both legacy entry points (run_legacy_eval+LegacyEnvServer) apply it via the v0env.set_kwargs.Usage
--id(notaskset) makesis_legacytrue;taskset/harness/runtime are ignored. Dict fields use dotted CLI syntax (--args.<k> <v>,--extra_env_kwargs.<k> <v>) or a@ config.toml.Verification
EvalConfig(id='reverse-text')→is_legacy=True,env_id='reverse-text'; native (taskset) →is_legacy=False.set_kwargsapplies cleanly. Native v1 path unchanged (gsm8k-v1subprocess → reward 1.0).ruff check/formatclean.Notes
openai_chat_completionsclient (eval needs no token ids), unlike the training bridge's renderer client.--id org/name[@version]installs the v0 env from the Environments Hub on demand (via feat: resolve taskset / harness / env ids from the Environments Hub #1597'sensure_installed); a local--id <name>must already be importable (uv pip install -e environments/<name>).id/args/extra_env_kwargs/is_legacy/env_idfrom prime-rl'sEnvConfigand inherit fromvf.EnvConfig; passextra_env_kwargstoLegacyEnvServerinenv_server.py(+ port main's auto-populate ofmax_total_completion_tokens/timeout_seconds/max_seq_len).Note
Medium Risk
Introduces a parallel eval execution path and shared legacy config on
EnvConfig, but native v1 eval is unchanged when a taskset is used; risk is mainly misconfiguration (legacy vs v1) and dependency on lazy v0 imports.Overview
Adds backwards-compatible v0 environment evaluation on the v1
uv run evalCLI via--id <env-id>(no taskset), aligned with prime-rl’s legacy env shape.EnvConfiggains legacy fields:id,args,extra_env_kwargs, plusis_legacy/env_idso eval and env-server configs can select a classicload_environmentenv instead of a v1 taskset/harness.The eval CLI treats
--idas a valid entry (alongside taskset or@ file.toml), branches onconfig.is_legacyto callrun_legacy_evalinstead of the native v1 path, and disables--richfor legacy runs (v1 rollout dashboard doesn’t apply).verifiers/v1/legacy.pywiresextra_env_kwargsthroughLegacyEnvServerviaenv.set_kwargs, and addsrun_legacy_eval: in-process v0 rollouts with bounded concurrency,RolloutOutput→Tracemapping, and the sameresults.jsonl/config.tomloutput layout as native eval (no env server / interception).Reviewed by Cursor Bugbot for commit adbcf95. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add legacy v0 environment support to the eval CLI via
--idflag--id <env-id>flag to the eval CLI (eval.py) that triggers a legacy v0 evaluation path, bypassing the v1 taskset/harness and suppressing the rich dashboard.run_legacy_evalin legacy.py to run v0 environments in-process: loads the env, samples tasks with optional deterministic shuffle and concurrency limits, runs rollouts, and persists results as v1Traceobjects tooutputs/<env>--<model>--legacy/<uuid>/results.jsonl.EnvConfigin env.py withid,args,extra_env_kwargsfields andis_legacy/env_idproperties to represent v0 environments alongside v1 taskset configs.--idis passed, SIGTERM handling and the v1 environment setup are skipped entirely.Macroscope summarized adbcf95.