You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Land the ArcticRL ⇄ verl integration as an arctic_platform/integrations/verl/ subpackage, plugged into verl core via the VERL_USE_EXTERNAL_MODULES hook. This unblocks the currently-stalled verl-project/verl#6422 by moving all Arctic-specific code out of the verl source tree while keeping the generic RemoteBackend abstraction in verl core.
Three commands. Zero verl fork required. Snowflake owns both the RL runtime (arctic_platform.rl) and the verl adapter in one repo.
Motivation
PR #6422 is stuck. Approved by @zw0610 on 2026-06-01, then on 2026-06-30 @wuxibin89 requested (a) port to the V1 trainer at verl/trainer/ppo/v1/trainer_base.py, and (b) "keep remote_backend abstraction in verl and separate all arctic_rl implementation to external module. All specific remote backend implementation should be dynamic plugin by hook VERL_USE_EXTERNAL_MODULES".
Precedent exists for framework-aware code in this repo.arctic_platform/rl/processors/verl_grpo.py (~440 LOC) is already a verl-shaped loss registered via arctic_platform.rl's pipeline. Formalizing this under integrations/verl/ cleans up an implicit habit.
Precedent exists for optional extras.pyproject.toml already ships [rl], [dev], [testing], [formatting]. Adding [verl] is one block, no new mechanism.
Owning both sides in one repo removes cross-repo lag. During PR RL correctness ports #3 review, adapter changes were blocked waiting for arctic_training async signatures (commit af1ab8d). Same repo = same PR.
Design
Package layout (added under arctic_platform/integrations/verl/)
arctic_platform/
integrations/ # NEW
__init__.py
verl/
__init__.py
register.py # ~15 LoC entry hook
adapter.py # ← was verl/workers/remote_client/arctic_rl.py (565 LoC)
rollout.py # ← was verl/workers/rollout/remote_rollout/arctic_rollout/ (339 LoC)
worker.py # ← was verl/remote_backend/workers/arctic_rl/worker.py (217 LoC)
grpo_loss.py # ← was arctic_platform/rl/processors/verl_grpo.py (~440 LoC)
config/arctic.yaml # ← was verl/trainer/config/remote_backend/arctic.yaml
examples/
run_bird_grpo_arl.sh
run_gsm8k_grpo_arl.sh
README.md
rl/
processors/verl_grpo.py # backward-compat shim: re-exports from integrations/verl/grpo_loss.py
pyproject.toml # +5 LoC: [verl] extra
register.py (the single file VERL_USE_EXTERNAL_MODULES imports)
[project.optional-dependencies]
verl = [\"hydra-core>=1.3\"] # verl itself is user-supplied; version pins live in launchers
Backward-compat shim
Any downstream user still doing from arctic_platform.rl.processors.verl_grpo import ... continues to work:
# arctic_platform/rl/processors/verl_grpo.py (after refactor)
\"\"\"Compat shim: verl-shaped loss now lives under integrations/verl/.\"\"\"fromarctic_platform.integrations.verl.grpo_lossimport*# noqa: F401, F403
Companion work (verl core)
This issue is scoped to arctic-platform. The paired verl-core change lands in verl-project/verl#6422:
Enforces RemoteBackend.requires_single_forwarder() (Samyam's original constraint).
Deletes verl/workers/remote_client/arctic_rl.py, verl/workers/rollout/remote_rollout/arctic_rollout/, verl/remote_backend/workers/arctic_rl/, verl/trainer/config/remote_backend/arctic.yaml, examples/arctic_rl/* from the PR diff.
Net verl-core diff: ~600 LoC added, ~1120 LoC removed (net negative — we're taking Arctic code out of verl core).
Deliverables
PR A — this repo (Arctic-Platform): add arctic_platform/integrations/verl/ package, add [verl] extra, add backward-compat shim for rl/processors/verl_grpo.py
verl/tests/remote_backend/test_v1_dispatch.py — mesh dispatch with world_size=1
Ship a MockRemoteBackend fixture in verl/remote_backend/testing.py
Layer 2 — Unit tests (this repo)
arctic_platform/tests/integrations/verl/test_register.py — VERL_USE_EXTERNAL_MODULES import triggers registration on both RemoteBackendRegistry and RolloutReplicaRegistry; lazy loaders defer heavy imports
Golden Run 3 — 8B BIRD, 20 steps. Reward monotone-increasing trend, no divergence, no OOM. Compare to arctic_v5 trajectory (~4h on 1 node / 8 H200).
Golden Run 4 (optional) — 32B BIRD, 4-node, 5 steps. Match AUTONOMOUS_STATUS.md steady-state timings within ±15%.
Merge safety guardrails
G1. Feature-flag default.trainer.remote_backend: null in ppo_trainer.yaml → exact same code path as today. Existing verl users cannot regress. Verified by an explicit test that patches RemoteBackendRegistry.create and asserts zero calls under default config.
G2. Backward-compat shim.arctic_platform.rl.processors.verl_grpo continues to resolve to the moved implementation. Shim tested in Layer 2.
G3. API-stability commitment.RemoteBackend ABC signature stays byte-identical to what @zw0610 approved on 2026-06-01. Any signature change requires a new PR.
G4. Reproducible run scripts committed with the PR.arctic_platform/integrations/verl/examples/run_*.sh are the exact scripts used for Golden Runs.
G5. Rollback plan. Pre-drafted single-commit revert PR sitting in draft on both repos. Package independence means arctic-platform / verl can be reverted separately.
G6. Canary before broadcast. Post-merge Day 0: kick off 32B BIRD convergence run against merged code. Auto-alert on reward drop >10% below reference. No external announcement until canary hits 100 steps clean.
Risks & mitigations
Risk
Probability
Impact
Mitigation
V1 hook lifecycle differs from what we modeled (_compute_old_log_prob async in some subclass)
Medium
+3–5d rework
Day-1 design spike reads trainer_sync.py, trainer_colocate_async.py, trainer_separate_async.py before writing code
TransferQueue incompatible with sync asyncio.run bridge
Medium
+1–2d
Mock TransferQueue in Layer 1 unit tests catches this before smoke
LLMServerClient wants to own the rollout server; our replica hosts its own vLLM
Medium
+2–3d
Read verl/workers/rollout/llm_server.py in Day-1 spike; if incompatible, shim through LLMServerClient-compatible facade in rollout replica
CUDA IPC weight sync breaks silently (seen before with ARCTIC_WEIGHT_SYNC_STRICT_NAMES=0)
Low
Reward → 0
Layer 4 Golden Run 1 catches this — reward at step 4 vs step 1 must show learning
Xibin has additional design requests on the V1-port PR (like zw0610 originally)
Medium
+1 week per round
Day-1 design DM to preempt
CLA signature delays (currently 4 of 5 committers unsigned on PR #6422)
High
+1 week per delay
Chase this week, escalate to legal on Day 2 if stalled
Downstream user imports arctic_platform.rl.processors.verl_grpo from unknown location
Low
External breakage
GitHub code search before releasing; keep shim indefinitely
Open questions (raise in the Xibin/Tunji/Zhi sync)
Recipe vs external-module?verl-project/verl-recipe/verl_tinker (landed 2026-07-03) uses the recipe pattern, not the VERL_USE_EXTERNAL_MODULES pattern. Would Xibin accept an arctic-platform-hosted plugin (this plan), or does he specifically want a verl-project/verl-recipe/arctic_rl/ recipe?
V1 trainer hook mapping. Are _compute_old_log_prob / _compute_ref_log_prob / _update_actor the right override points, or should we implement TrainingWorker / BaseEngine instead (the v0.7 blog described TrainingWorker as "Tinker-like")?
CLA path. Chase all 4 unsigned committers, or squash+re-author PR #6422 under @sfc-gh-truwase only (fastest merge)?
Summary
Land the ArcticRL ⇄ verl integration as an
arctic_platform/integrations/verl/subpackage, plugged into verl core via theVERL_USE_EXTERNAL_MODULEShook. This unblocks the currently-stalled verl-project/verl#6422 by moving all Arctic-specific code out of the verl source tree while keeping the genericRemoteBackendabstraction in verl core.Target UX:
pip install arctic-platform[verl] export VERL_USE_EXTERNAL_MODULES=arctic_platform.integrations.verl.register verl train ... trainer.remote_backend=arcticThree commands. Zero verl fork required. Snowflake owns both the RL runtime (
arctic_platform.rl) and the verl adapter in one repo.Motivation
@zw0610on 2026-06-01, then on 2026-06-30@wuxibin89requested (a) port to the V1 trainer atverl/trainer/ppo/v1/trainer_base.py, and (b) "keepremote_backendabstraction in verl and separate allarctic_rlimplementation to external module. All specific remote backend implementation should be dynamic plugin by hookVERL_USE_EXTERNAL_MODULES".arctic_platform/rl/processors/verl_grpo.py(~440 LOC) is already a verl-shaped loss registered viaarctic_platform.rl's pipeline. Formalizing this underintegrations/verl/cleans up an implicit habit.pyproject.tomlalready ships[rl],[dev],[testing],[formatting]. Adding[verl]is one block, no new mechanism.arctic_trainingasync signatures (commitaf1ab8d). Same repo = same PR.Design
Package layout (added under
arctic_platform/integrations/verl/)register.py(the single fileVERL_USE_EXTERNAL_MODULESimports)pyproject.tomladditionBackward-compat shim
Any downstream user still doing
from arctic_platform.rl.processors.verl_grpo import ...continues to work:Companion work (verl core)
This issue is scoped to arctic-platform. The paired verl-core change lands in verl-project/verl#6422:
verl/remote_backend/{base,trainer_v1,worker,worker_utils}.py— ABC + registry + V1-hook-based trainer + generic forwarder worker. Zero Arctic-specific code.RemoteBackendTrainerfromRayPPOTrainersubclass →PPOTrainerV1-hook subclass, overriding_compute_old_log_prob,_compute_ref_log_prob,_update_actor,_save_checkpoint.RemoteBackend.requires_single_forwarder()(Samyam's original constraint).verl/workers/remote_client/arctic_rl.py,verl/workers/rollout/remote_rollout/arctic_rollout/,verl/remote_backend/workers/arctic_rl/,verl/trainer/config/remote_backend/arctic.yaml,examples/arctic_rl/*from the PR diff.Net verl-core diff: ~600 LoC added, ~1120 LoC removed (net negative — we're taking Arctic code out of verl core).
Deliverables
arctic_platform/integrations/verl/package, add[verl]extra, add backward-compat shim forrl/processors/verl_grpo.pyTimeline
Engineering: 5–7 days. Calendar: ~2 weeks with normal review latency.
PPOTrainer,TrainingWorker,LLMServerClientend-to-end; DM 1-page design proposal to@wuxibin89/@zw0610integrations/verl/scaffolding + backward-compat shim +[verl]extraremote_backend/trainer.pyas V1 hooks againstPPOTrainermain, delete arctic files, add V1 trainer, updatemain_ppo.pyroutingTesting strategy
Layered from cheap-static to expensive-E2E. Each layer catches a specific class of bug.
Layer 0 — Static (blocks PR)
pre-commitpasses (ruff, ruff-format, mypy, license, docstrings)Layer 1 — Unit tests (verl PR)
verl/tests/remote_backend/test_registry.py— register/get/create round-trip, duplicate register, lazy loadverl/tests/remote_backend/test_trainer_asserts.py—_enforce_single_forwarder_if_required()matrixverl/tests/remote_backend/test_hooks_lifecycle.py— V1 hook fire order,destroy()on exception pathsverl/tests/remote_backend/test_worker_utils.py—left_pad_to,make_njt,normalize_backend_metricsverl/tests/remote_backend/test_v1_dispatch.py— mesh dispatch withworld_size=1MockRemoteBackendfixture inverl/remote_backend/testing.pyLayer 2 — Unit tests (this repo)
arctic_platform/tests/integrations/verl/test_register.py—VERL_USE_EXTERNAL_MODULESimport triggers registration on bothRemoteBackendRegistryandRolloutReplicaRegistry; lazy loaders defer heavy importsarctic_platform/tests/integrations/verl/test_adapter.py—ArcticRLClientWrapper.from_config(),reconnect_handle(), idempotentdestroy()arctic_platform/tests/integrations/verl/test_payload.py— golden-file snapshot of_prepare_padded_arctic_batch_dict()wire format (catches wire regressions immediately)arctic_platform/tests/integrations/verl/test_backward_compat.py—arctic_platform.rl.processors.verl_grpoimport still worksLayer 3 — Cross-repo integration
test_e2e_mock.pyin verl PR — 4-step train loop againstMockRemoteBackend, CPU only, ~2 minarctic_platform's@pytest.mark.integration_verljob — pins verl PR branch, usesMockArcticServer(in-process stub ofArcticRLRayClient), ~5 minLayer 4 — E2E smoke (real Arctic backend, GPU)
Reference numbers to match, snapshotted from proven-good runs before any refactor:
arctic_v5trajectory (~4h on 1 node / 8 H200).AUTONOMOUS_STATUS.mdsteady-state timings within ±15%.Merge safety guardrails
trainer.remote_backend: nullinppo_trainer.yaml→ exact same code path as today. Existing verl users cannot regress. Verified by an explicit test that patchesRemoteBackendRegistry.createand asserts zero calls under default config.arctic_platform.rl.processors.verl_grpocontinues to resolve to the moved implementation. Shim tested in Layer 2.RemoteBackendABC signature stays byte-identical to what@zw0610approved on 2026-06-01. Any signature change requires a new PR.arctic_platform/integrations/verl/examples/run_*.share the exact scripts used for Golden Runs.Risks & mitigations
_compute_old_log_probasync in some subclass)trainer_sync.py,trainer_colocate_async.py,trainer_separate_async.pybefore writing codeTransferQueueincompatible with syncasyncio.runbridgeLLMServerClientwants to own the rollout server; our replica hosts its own vLLMverl/workers/rollout/llm_server.pyin Day-1 spike; if incompatible, shim throughLLMServerClient-compatible facade in rollout replicaARCTIC_WEIGHT_SYNC_STRICT_NAMES=0)arctic_platform.rl.processors.verl_grpofrom unknown locationOpen questions (raise in the Xibin/Tunji/Zhi sync)
VERL_USE_EXTERNAL_MODULESpattern. Would Xibin accept an arctic-platform-hosted plugin (this plan), or does he specifically want averl-project/verl-recipe/arctic_rl/recipe?_compute_old_log_prob/_compute_ref_log_prob/_update_actorthe right override points, or should we implementTrainingWorker/BaseEngineinstead (the v0.7 blog describedTrainingWorkeras "Tinker-like")?@sfc-gh-truwaseonly (fastest merge)?References
/cc @sfc-gh-truwase @sfc-gh-sbekman