feat: vf v1 <> nano bridge - #2742
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Points the submodule at the vf-nano EnvServer branch so the orchestrator can build on the env-server abstraction. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch prime-rl's env path to vf-nano: the orchestrator spawns a vf-nano EnvServer per env (it never loads an environment), dispatches rollouts by task index, and trains on the returned Trace dicts (branches + renderer tokens). - pyproject: dep verifiers -> vf-nano; drop v1/research env packages; only the vf-nano reverse-text example; override out the transitive v1 verifiers (pulled by the prime CLI) so it can't shadow vf-nano's `verifiers` package; add orjson /pandas/msgspec (were transitive via verifiers). - EnvConfig inherits vf-nano's swappable agent/runtime (+ max_turns). - envs.py: spawn EnvServer child + EnvClient, info() for num_tasks/group-scoring, dispatch by task_idx, adapt Trace -> RolloutOutput-shaped dict. - trajectories.py: trace_to_samples (one sample per Trace branch) + trace_to_output. - train_source: index sampling; client pool builds vf-nano ClientConfig; lag monitor vendored; env-server entrypoint repointed; ~14 files retyped off vf.RolloutOutput / vf.ClientConfig. - configs/debug/vf_nano_reverse_text.toml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er config)
- trace_to_samples stitches each Trace branch's tokens into one TrainingSample
(prompt = branch start, then each turn's new context [masked] + generated
tokens [trained]); drop the RolloutOutput adapter — read the Trace's native
fields directly (reward, error{type,message}, timing generation/scoring,
num_turns, branches).
- envs returns the raw Trace; eval_sink / train_sink / dispatcher / metrics /
orchestrator read native Trace fields (no token_usage/completion/timing.total).
- client pool forwards the shared renderers.RendererConfig to the env server's
renderer client (so it uses qwen3, not the tool-less default fallback).
- debug config: tool_call_parser=hermes (vLLM accepts the agent's tools),
max_steps=20.
- bump deps/vf-nano.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o timeout) - Env.run_rollout/run_group pass the vf-nano ClientConfig object and a SamplingConfig (built from the env's sampling args) directly — no model_dump, no per-rollout timeout forwarded to the server. - debug config: max_steps=20. - bump deps/vf-nano (typed env-server RPC).
The env server returns a Trace minus its derived fields; the orchestrator resolves
the env's Task subclass (from config.id) and validates the wire dict into a strict
Trace[EnvTask], so the whole orchestrator works with a real, typed vf.Trace —
typed task fields included (e.g. task.answer), nothing subscriptable.
- envs.py: resolve_task_type(env_id); run_rollout/run_group validate -> Trace[EnvTask].
- trajectories/types/dispatcher/train_sink/eval_sink/metrics/filters/advantage/utils
/orchestrator: attribute access on the typed Trace (reward, error{type,message},
branches, timing.<span>.duration, num_turns, ...); derived fields recompute on the
consumer.
- Task/Trace/TimeSpan stay strict (StrictBaseModel) — no extra=ignore anywhere.
- bump deps/vf-nano.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The orchestrator spawns the env server, so request the serve extra (zmq/msgpack) explicitly now that vf-nano keeps them out of core. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`from __future__ import annotations` already defers all annotations to strings, so the quotes + `# noqa: F821` on the TYPE_CHECKING-only `vf.Trace` / `TrainRollout` annotations are unnecessary (no import cycle — verifiers.nano never imports prime_rl). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The field holds a typed vf.Trace, so `trace` reads truer than `raw` (which suggested an unparsed dict). Renames the field + every `.raw` access, the `emit_rollout(trace=...)` param/kwarg, the to_dict field filter, and the dispatcher cancel-path locals. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Drop the FinishedRollout proxy properties (error/reward/is_truncated and the
example_id field); consumers now read r.trace.{reward,is_truncated,task.idx,...}
directly. The trace is the single source of truth.
- Use vf.Trace.has_error for existence checks instead of `.error is not None`.
- Replace the prime-rl trace_* token-length utils with vf.Trace.{completion_len,
total_tokens,has_response} (now on the trace); keep trace_to_samples.
- Carry task_idx end-to-end (GroupState.task_idx, env.run_rollout/run_group(task_idx),
source dict key) instead of the example/example_id dict carrier; identity comes
off trace.task.idx.
- Mark the local-package env arrangement as a temporary/experimental TODO.
- Move the debug config to configs/debug/nano/reverse_text.toml.
- Bump deps/vf-nano (Trace/Turn accessors).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- The env server binds tcp://127.0.0.1:0 and reports its concrete address back over a queue; the orchestrator connects to that. Removes _get_free_port and its TOCTOU race (the OS assigns the port atomically). - A spawned server has already bound + loaded by the time it reports its address, so the untimed info() is enough — only poll wait_for_server_startup for an external (config.address) server, which has no spawn handshake. - Bump deps/vf-nano (port report + Trace/Branch token-length accessors). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Task-subclass introspection now lives in vf-nano (vf.task_type); drop the prime-rl copy and build the typed Trace via vf.Trace[vf.task_type(env_id)]. Bump deps/vf-nano. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SFT trains on a teacher served over the chat client, which returns no token ids, so the trace's turns have tokens=None and trace_to_samples yields nothing. Restore backfill: for each tokenless turn, render its prompt + assistant response with the student chat template and split on the longest common prefix to fill TurnTokens (masks/logprobs come from trace_to_samples). train_sink.process_rollout backfills when any turn lacks tokens, before building samples. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
drop_group's error_rollout_output calls omitted the required task_idx, so an off-policy cancel (on_new_version) raised TypeError. Use the group's task_idx (or -1 when the group is already gone), mirroring handle_completed_rollout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- envs.py: EnvClient now returns Trace[WireTask]; upgrade to this env's real Task subclass via self.trace_type.model_validate(wire.to_wire()). - dispatcher.py: drop the error_rollout_output helper — inline the synthetic error Trace at each call site using vf.Error's field names (type/message/traceback); the task-exception path carries a real traceback, cancels/empty-trajectory carry none. - Bump deps/vf-nano. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nical - Spawned env servers now route their output (logging + subprocess-runtime output) to <output_dir>/logs/envs/<name>.log via a _run_env_server wrapper that redirects stdout/stderr and sets up logging in the child. Previously the orchestrator-spawned server logged nowhere. - Debug config: batch_size 16->128, group_size 8->16, eval num_examples 8->128 (interval=1), matching configs/debug/training_modes/rl.toml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The orchestrator already passes a train/eval-split log_dir (.../logs/envs/train, .../logs/envs/eval), so _spawn must drop the file directly under it (<log_dir>/<name>.log) rather than re-adding an envs/ subdir — which had buried the train/eval split under logs/envs/<kind>/envs/<name>.log. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Instead of the orchestrator sidecar-spawning each env server as an mp child, the
rl launcher now spawns one `env-server` process per env (train + eval), each on a
free port, with output to logs/envs/{kind}/{name}.log and a crash monitor — same
model as inference/trainer. It sets env.address in the orchestrator config so the
orchestrator attaches (its existing external path) instead of spawning. Envs that
already set address (user-managed external server) are left alone; the orchestrator's
mp sidecar stays as the fallback for running `orchestrator` directly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add RLConfig.env_server_base_port (default 5000); the i-th launcher-managed env binds base_port + i. Drops the get_free_port dependency in the launcher. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Train envs bind base_port + i; eval envs bind base_port + ENV_SERVER_KIND_STRIDE + i (stride 1000), so each kind has headroom for many envs without the blocks colliding (was a single running index — train and eval sat adjacent). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- env_server entrypoint: intercept vf-nano stdlib logging so the server's own logs (EnvServer up, request failures) land in logs/envs/<kind>/<name>.log — previously only loguru output was captured, swallowing them. - envs.py: close the address-handoff mp.Queue after use (no resource_tracker leaked-semaphore warning on the sidecar path). - configs/debug/nano/reverse_text.toml: drop the eval block, mirroring examples/reverse_text/rl.toml (train-only smoke; eval path validated separately). - bump deps/vf-nano (serve/types docstring trim). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…irectly The I/O boundary (save_rollouts + monitor sample tables) now dumps the typed vf.Trace itself (r.trace.model_dump(mode="json")) instead of a Trace+metadata merge — the on-disk rollout is just the trace. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
vf-nano renamed its rollout-driver abstraction Agent -> Harness. Update the
integration: EnvConfig.agent -> harness (HarnessConfig/DefaultHarnessConfig);
env.run_rollout/run_group spawn forwards harness_config; the env-server entrypoint
passes harness_config/harness_timeout; debug config uses `harness = {...}`. Bump
deps/vf-nano to the renamed branch.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TrainConfig.env defaulted to an empty list with no check (only eval envs were validated non-empty), so a config with no [[orchestrator.train.env]] parsed and only failed later in TrainSource during setup. Add a non-empty-env validator (mirroring EvalConfig) and make OrchestratorConfig.train a required field (no default), so the failure surfaces at parse / --dry-run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch the scaleswe debug config to the rlm harness on prime sandboxes for both train (scaleswe-v1) and eval (swebench-verified) envs, and enable router replay (trainer.enable_router_replay + inference.enable_return_routed_experts, mirroring configs/debug/r3.toml) to cut trainer/inference MoE routing mismatch on the A3B model. Rename run/job to the -rlm variant and sync the prime sandbox cleanup labels. Header comment corrected (prime, not modal). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The train-required validation (60e8242) broke ~18 config-propagation tests in test_configs.py that construct OrchestratorConfig/RLConfig without a train env, plus two checked-in configs that omit one (configs/debug/orch.toml, examples/glm5_pd_disag/rl.toml). A config-level validator is the wrong place — the codebase widely constructs train-less OrchestratorConfigs. Reverting to green CI; a non-empty-train guard belongs at orchestrator setup (fail fast before TrainSource) instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Bump deps/verifiers b841e53a -> 7169d02d (latest verifiers main, clean fast-forward, ...#1838). Editable path dep with unchanged declared deps, so uv.lock is unchanged. Verified with reverse_text v1 (reward 0.13 -> 0.75 over 10 steps, no errors). - Remove the stale "no prime-env hub install" TODO from the orchestrator — it never loads environments (the env server imports them in its child process), so the note no longer applies. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Namespaced legacy env auto-install was removed (the v1 env server loads envs directly via serve_env), leaving dead code with no callers/readers: the `env_install_prerelease` flags (OrchestratorConfig + EnvServerConfig), the `install_env` helper, and `get_env_ids_to_install`. Drop them and the now- orphaned `subprocess` / `EnvConfig`,`EvalEnvConfig` imports. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
samsja
reviewed
Jun 23, 2026
The orchestrator no longer spawns env servers in-process (the mp.Process + os.dup2 fd-redirect path). It only attaches to a server at EnvConfig.address, now mandatory at orchestrator runtime - Env.start errors if it's unset. The rl launcher already spawns one env-server subprocess per env and assigns address, so launcher-driven runs are unaffected; standalone `uv run orchestrator` must now be paired with separately-launched env-server(s) and an explicit address per env. Removes _run_env_server, Env._spawn, Env/Envs.shutdown, _env_server_process, and the now-dead mp/os/queue/sys/atexit imports + ENV_SERVER_SPAWN_TIMEOUT. Updates the standalone-orchestrator docs (training.md, scaling.md --bench) and the CHANGELOG. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…etup) 51701f4c (#1846). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This reverts commit d775461. Restore the in-process env-server spawn (orchestrator sidecar-spawns when address is unset) to mirror main for now. The address-required / attach-only cleanup is deferred - revisit later. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
samsja
previously approved these changes
Jun 23, 2026
…main) Drop the launcher's env-server management: setup_env_servers (which pre-assigned each env an address + spawned an env-server subprocess), the per-env subprocess spawn loop in rl_local, the ENV_SERVER_KIND_STRIDE constant, and the env_server_base_port config field. With no address pre-assigned, the orchestrator spawns its own v1 EnvServer in-process per env (Env._spawn), exactly as on main. The env-server CLI and EnvConfig.address are kept for the optional user-managed external-server path. 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 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 19c88f5. Configure here.
…ile (#2860) Router replay passes routed_experts[:, :, layer_idx, :] (a strided view of [tokens, layers, topk], dim-1 stride = layers*topk) into the MoE forward. Under torch.compile the inductor kernel asserts a contiguous input, so it crashed with `assert_size_stride ... stride 8==368` on GLM-4.5-Air. Make the slice contiguous. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7c347f61 (#1848). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
hallerite
approved these changes
Jun 24, 2026
samsja
approved these changes
Jun 24, 2026
hallerite
added a commit
that referenced
this pull request
Jun 24, 2026
…2742) Integrate #2746's algorithm abstraction onto #2742's verifiers-v1 pipeline rewrite. #2746's component-loss model (per-token rl/ce/ref_kl weight streams + a per-token advantages stream, summed and globally normalized) supersedes main's training_mode loss dispatch; teacher_logprobs -> ref_logprobs; the scalar advantage becomes a full-length-N per-token stream gated by the mask. - types: Rollout stays a vf.Trace subclass; scalar advantage -> per-token advantages; RolloutView wraps Rollout (`raw` returns the Trace itself). - algo/: routing/opd/opsd/echo ported from prompt/completion split + dict trajectory to flat token_ids/mask + vf.Trace branches/nodes. echo weights later-turn observation nodes by role; opsd re-renders the demo prefix via the renderer; opd/opsd prefill the flat token sequence into ref_logprobs. - orchestrator: the three scoring hooks fire at process_rollout / process_group / finalize_batch; per-env Sampler+Algorithm replace the global training_mode + student/teacher; the standalone teacher pool is gone (algorithms connect their own frozen pools). advantage.py deleted. - trainer/batch: flat-token reads + component streams + NANO MM-aware cut; loss/packer/dispatcher/metrics auto-merged and verified. - configs: EnvConfig(vf.EnvServerConfig), algo: AlgorithmConfig + model: HostedModelConfig, renderer required (MITO removed); dropped training_mode/teacher/advantage and the NANO advantage classes. - fixed latent breaks in utils/monitor/prime.py (scalar advantage, student). Verified: all 18 orchestrator/trainer/config modules import under v1; the full AlgorithmConfig union + build_algorithm work; ruff clean; the rl config dry-run generates the algo + env-server subconfigs; ~410 unit tests green (orchestrator 97, configs incl. the full TOML sweep 122, rest 316). NOTE: committed UNSIGNED — the forwarded ssh-agent for commit signing is dead (ssh_auth_sock points at a closed socket; no agent has the key). Re-sign with `git commit --amend -S` once the agent is restored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 1, 2026
hallerite
added a commit
that referenced
this pull request
Jul 1, 2026
Dead since the v1<>nano bridge (#2742) deleted the forwarding: the v1 EnvClient has no max_retries parameter, so configured values silently did nothing. Retry behavior is owned by verifiers' retries: RetryConfig on the v1 Env definition. Co-Authored-By: Claude Fable 5 <noreply@anthropic.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.

Companion PR to PrimeIntellect-ai/verifiers#1576 for verifiers v1 training integration.
Summary
Integrates verifiers v1 into prime-rl. Training rolls out against a v1 env server that returns
vf.Tracerollouts, with renderer-only (token-in/out) training and a unifiedtoken_idssample schema.vfEnvServerper environment (subprocess runtime, ZMQ) and drives it purely by task index — it never loads the env. v0 envs run through the verifiers legacy bridge; native v1 tasksets serve directly; both returnvf.Traceover one protocol.vf.Tracerollouts.Rolloutis avf.Trace[TaskT]; prime-rl's orchestration metadata lives on it asexclude=Truefields, so a dumped rollout is a plain trace. The wire trace is re-typed intoRolloutviamodel_construct(no serialize round-trip of the node tensors).token_idsschema. Rollouts are tokenized renderer-side into oneTrainingSample/MicroBatchrepresentation (replaces the prompt/completion split); main'sbin_costpacking andMicroBatch.sequence_lengthsare grafted onto it.EnvConfigextendsvf.EnvServerConfig(taskset/harness/runtime/pool);EvalClientConfig/TrainClientConfigreplace the prior client config.main(v0.6.0) merged in.Verification
Scaleswe v1 (W&B)
Hendrycks v0/v1 (W&B)
General Agent v1 on Modal (W&B)
Wiki Search v0 (W&B)
Alphabet Sort v0 (W&B)
Breaking
ClientConfig.timeout/ClientConfig.connect_timeoutremoved. They were no longer wired into rollout clients (the v1 client manages its own timeouts). Remove them from configs.vf.Trace, not a raw dict.CustomAdvantageConfigfunctions must use attribute access (r.reward,r.completion_len,r.nodes) instead of dict keys (r["reward"]).openai/gpt-5-mini) is no longer supported — SFT teachers must be a local-vLLM or v0-bridge env that returns tokens. Teacher-less SFT now errors; use the standalonesftentrypoint.[[orchestrator.train.env]]takestaskset = { id = … }+harness = { id = …, runtime = { type = … } }(extendsvf.EnvServerConfig); a bare v0 env id runs through the legacy bridge.trainer/mismatch_klandtrainer/entropyare no longer folded into W&B (the trainer still writes the token-export JSONL whenenable_token_export = True).prime env installfor namespaced legacy env IDs (e.g.primeintellect/wordle); the v1 env server (serve_env) loads envs directly. Runs that relied on automatic install — including the Wordle legacy-bridge config — fail unless the env is installed manually (uv run prime env install <id>) beforehand. Theenv_install_prereleaseconfig flags and theinstall_envhelper are removed.Note
High Risk
Large orchestrator rewrite (env serving, rollout schema, training sample construction) touches the core RL loop and breaks multiple config contracts; misconfiguration or bridge edge cases could silently drop or mis-tokenize rollouts.
Overview
Integrates verifiers v1 so the orchestrator trains on
vf.Tracerollouts from a spawned or external v1 env server, driven by task index instead of loading envs in-process. v0 envs still work via the legacy bridge; native configs usetaskset+harness+poolonEnvConfig(extendsvf.EnvServerConfig).Rollout and training path:
Rolloutis now a typedvf.Tracewith orchestration metadata onexclude=Truefields (replacingTrainRollout/EvalRollout+ rawRolloutOutputdicts). Dispatcher, sinks, filters, and advantages use trace attributes (reward,nodes,completion_len,has_error).trajectories.pyis rewritten:trace_to_samplesbuilds oneTrainingSampleper branch from flattoken_ids/mask/logprobs(no MITO backfill or trajectory interleaving). Renderer is required for all modes; SFT teachers use the renderer client for tokens. Startupprime env installand orchestrator token-export W&B drain are removed.Config and deps: Train/eval env lists default empty (no implicit
reverse-text);num_workersmigrates topool; clienttimeout/connect_timeoutdropped; slimprime-rl-configsmay depend onverifiersv1 types. Adds v1 debug configs and bumps theverifierssubmodule; removes external SFT example config.Reviewed by Cursor Bugbot for commit 5056e27. Bugbot is set up for automated code reviews on this repo. Configure here.