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
Tracking issue for Cortex-serverless enablement of the two merged upstream RL integrations — NovaSky-AI/SkyRL#1837 (integrations/arctic_rl/) and verl-project/verl#6422 (Arctic-specific RemoteBackend in arctic_platform/integrations/verl/adapter.py) — without changing either integration's code.
Design principle
Both integrations construct their RL client via exactly one call:
Both hardcode backend="local" in that construction (SkyRL config.py:570, verl adapter.py:559), so a yaml-level backend=cortex is a silent no-op today. The bridge: an env-var override inside create_arctic_rl_client that rewrites the incoming config to Cortex when the launcher exports ARCTIC_RL_BACKEND=cortex. Zero adapter change.
Scope on the Arctic-Platform side (done or in flight)
1. Unified client + Cortex transport (Mike, done or in review)
#45: arctic_platform.client.ArcticRLClient + Transport ABC + on-prem RayTransport.
Response-shape shim in CortexTransport._shape_train_response (aliases model_outputs → batch, mirrors loss ↔ avg_loss, bubbles metrics.grad_norm to top level so .get("grad_norm") works uniformly across on-prem and Cortex).
On-prem parity fields threaded through the unified init (ds_worker_config, arctic_inference_config, log_prob_ds_config, full_determinism).
RayTransport accepts a server_state= parameter so verl's driver → Ray-worker reconnect flow keeps working.
arctic_platform.rl.create_arctic_rl_client(config, server_state) dispatches to Cortex when config.backend == "cortex": translates to a unified config, constructs an arctic_platform.client.ArcticRLClient, and wraps it in _CortexClientShim that re-exposes the exact async surface both integrations reach for. 29 pinning tests in tests/client/test_rl_cortex_dispatch.py.
Env-var override in the same factory. Both adapters hardcode backend="local"; the launcher exports ARCTIC_RL_BACKEND=cortex + CORTEX_* and the factory rewrites the config before dispatch. 6 tests under TestEnvOverride.
Lazy arctic_platform/rl/__init__.py (PEP 562 __getattr__). Cortex users install only arctic-platform + pydantic + requests; no ray / vllm / arctic_inference / torch required on the driver.
Fake Cortex GS + 18-test E2E plumbing smoke at tests/e2e/. Speaks every REST route the client's CortexTransport actually hits, decodes DSSST1 chunked uploads, returns shape-plausible canned responses (random losses, shape-correct logprobs tensors). Doubles as an executable interface reference for the real Cortex-training endpoint. Total 116 tests pass locally on a driver with only pydantic + requests + fastapi + uvicorn + safetensors + torch installed.
3. Cortex server-side (Neutrino GS team) — blocking real Cortex training convergence
The client PR ships shims (log a None for missing metrics.grad_norm, _colo_noop for lifecycle ops Cortex doesn't have) so nothing crashes when these are absent, but a real Cortex-backed training step needs:
POST /{job_id}/forward-no-grad — symmetric to forward-backward. Accept {args, kwargs, meta, processing, reference_model?} (DSSST1 chunks in) and return {model_outputs: {logprobs, entropy?}, metrics: {...}} (DSSST1 out). verl calls this twice per batch (actor + ref); SkyRL calls it in the logprobs post-processor path. Runnable spec: fake_cortex_gs.py.
POST /{job_id}/log-probs — JSON in / DSSST1 out. Symmetric to generate in framing. Optional for verl; blocking for any recipe that scores completions server-side.
Grow metrics: {grad_norm, ppo_kl, pg_clipfrac_lower, pg_loss, kl_loss, kl_coef} on fwd_bwd / step responses. Client currently None-shims these; both integrations reduce them into wandb.
Batch contract validation: Cortex Neutrino trainer proto is additionalProperties: true, so pre-tokenized kwargs={input_ids, attention_mask, position_ids, prompts, responses, response_mask, advantages, old_log_probs, ref_log_prob?} should round-trip today. Needs an end-to-end call on a real cluster to catch any silently-dropped keys.
4. Full E2E validation (parallel-track, needs both sides)
On-prem regression smoke — run arctic_platform/integrations/verl/examples/run_gsm8k_grpo_arl.sh unmodified with backend: local (default) on 1× H200. Loss curves should match main within numerical noise. Only relevant if we're worried about feat(rl): Cortex-serverless dispatch via arctic_platform.rl (zero integration change) #50 perturbing the default path; the unit tests cover the surface but there's no substitute for a real training run. Requires verl + arctic-inference[vllm] + tensordict installed.
Cortex first run — same recipe with ARCTIC_RL_BACKEND=cortex + a real Cortex endpoint. Unlocks once §3 items land.
What we deliberately are not doing
No integration-side patches. Neither NovaSky-AI/SkyRL#1837:integrations/arctic_rl/ nor arctic_platform/integrations/verl/adapter.py gets any import swap, await restructure, or config schema change.
No fork of arctic_platform.rl. The legacy on-prem client stays as-is; the Cortex path lives behind a single early-branch in create_arctic_rl_client plus the env-var override.
No dep changes for on-prem users. The lazy __init__ refactor keeps every arctic_platform.rl.* export addressable; the only difference is when the underlying module is loaded. Existing test_cpu_import.py still passes.
User-facing UX (what the two integrations flip to go serverless)
SkyRL — set these in the recipe launcher (existing script, no code change):
Tracking issue for Cortex-serverless enablement of the two merged upstream RL integrations — NovaSky-AI/SkyRL#1837 (
integrations/arctic_rl/) and verl-project/verl#6422 (Arctic-specificRemoteBackendinarctic_platform/integrations/verl/adapter.py) — without changing either integration's code.Design principle
Both integrations construct their RL client via exactly one call:
Both hardcode
backend="local"in that construction (SkyRLconfig.py:570, verladapter.py:559), so a yaml-levelbackend=cortexis a silent no-op today. The bridge: an env-var override insidecreate_arctic_rl_clientthat rewrites the incoming config to Cortex when the launcher exportsARCTIC_RL_BACKEND=cortex. Zero adapter change.Scope on the Arctic-Platform side (done or in flight)
1. Unified client + Cortex transport (Mike, done or in review)
arctic_platform.client.ArcticRLClient+TransportABC + on-premRayTransport.HttpTransport.CortexTransport+ DSSST1 safetensors wire codec. Open, MERGEABLE,mergeStateStatus=BLOCKED. feat(rl): Cortex-serverless dispatch via arctic_platform.rl (zero integration change) #50 stacks on this.2. Compat +
arctic_platform.rldispatch + env-var override + fake Cortex GS — #50 (draft)Stacked on #47. All changes on the Arctic-Platform side; zero integration touch.
arctic_platform.client(legacy backend aliases, response-shape flattening, colocation-lifecycle ops,**legacy_kwargsfolding onfwd_bwd/fwd_no_grad,sync_weights(cuda_ipc, low_memory),training_job_id/sampling_job_id/log_prob_job_id/get_server_stateproperties). 26 pinning tests intests/client/test_skyrl_verl_compat.py.CortexTransport._shape_train_response(aliasesmodel_outputs→batch, mirrorsloss↔avg_loss, bubblesmetrics.grad_normto top level so.get("grad_norm")works uniformly across on-prem and Cortex).ds_worker_config,arctic_inference_config,log_prob_ds_config,full_determinism).RayTransportaccepts aserver_state=parameter so verl's driver → Ray-worker reconnect flow keeps working.arctic_platform.rl.create_arctic_rl_client(config, server_state)dispatches to Cortex whenconfig.backend == "cortex": translates to a unified config, constructs anarctic_platform.client.ArcticRLClient, and wraps it in_CortexClientShimthat re-exposes the exact async surface both integrations reach for. 29 pinning tests intests/client/test_rl_cortex_dispatch.py.backend="local"; the launcher exportsARCTIC_RL_BACKEND=cortex+CORTEX_*and the factory rewrites the config before dispatch. 6 tests underTestEnvOverride.arctic_platform/rl/__init__.py(PEP 562__getattr__). Cortex users install onlyarctic-platform+pydantic+requests; no ray / vllm / arctic_inference / torch required on the driver.tests/e2e/. Speaks every REST route the client'sCortexTransportactually hits, decodes DSSST1 chunked uploads, returns shape-plausible canned responses (random losses, shape-correct logprobs tensors). Doubles as an executable interface reference for the real Cortex-training endpoint. Total 116 tests pass locally on a driver with only pydantic + requests + fastapi + uvicorn + safetensors + torch installed.3. Cortex server-side (Neutrino GS team) — blocking real Cortex training convergence
The client PR ships shims (log a
Nonefor missingmetrics.grad_norm,_colo_noopfor lifecycle ops Cortex doesn't have) so nothing crashes when these are absent, but a real Cortex-backed training step needs:POST /{job_id}/forward-no-grad— symmetric toforward-backward. Accept{args, kwargs, meta, processing, reference_model?}(DSSST1 chunks in) and return{model_outputs: {logprobs, entropy?}, metrics: {...}}(DSSST1 out). verl calls this twice per batch (actor + ref); SkyRL calls it in thelogprobspost-processor path. Runnable spec:fake_cortex_gs.py.POST /{job_id}/log-probs— JSON in / DSSST1 out. Symmetric togeneratein framing. Optional for verl; blocking for any recipe that scores completions server-side.metrics: {grad_norm, ppo_kl, pg_clipfrac_lower, pg_loss, kl_loss, kl_coef}onfwd_bwd/stepresponses. Client currentlyNone-shims these; both integrations reduce them into wandb.additionalProperties: true, so pre-tokenizedkwargs={input_ids, attention_mask, position_ids, prompts, responses, response_mask, advantages, old_log_probs, ref_log_prob?}should round-trip today. Needs an end-to-end call on a real cluster to catch any silently-dropped keys.4. Full E2E validation (parallel-track, needs both sides)
arctic_platform/integrations/verl/examples/run_gsm8k_grpo_arl.shunmodified withbackend: local(default) on 1× H200. Loss curves should matchmainwithin numerical noise. Only relevant if we're worried about feat(rl): Cortex-serverless dispatch via arctic_platform.rl (zero integration change) #50 perturbing the default path; the unit tests cover the surface but there's no substitute for a real training run. Requiresverl+arctic-inference[vllm]+tensordictinstalled.ARCTIC_RL_BACKEND=cortex+ a real Cortex endpoint. Unlocks once §3 items land.What we deliberately are not doing
NovaSky-AI/SkyRL#1837:integrations/arctic_rl/norarctic_platform/integrations/verl/adapter.pygets any import swap,awaitrestructure, or config schema change.arctic_platform.rl. The legacy on-prem client stays as-is; the Cortex path lives behind a single early-branch increate_arctic_rl_clientplus the env-var override.__init__refactor keeps everyarctic_platform.rl.*export addressable; the only difference is when the underlying module is loaded. Existingtest_cpu_import.pystill passes.User-facing UX (what the two integrations flip to go serverless)
SkyRL — set these in the recipe launcher (existing script, no code change):
verl — same env-var pattern, same launcher:
Neither adapter's yaml needs a new field, and both continue to hardcode
backend="local"internally — the factory rewrites it.Rollout order
ARCTIC_RL_BACKEND=cortexfor the first end-to-end serverless smoke.References
tests/e2e/fake_cortex_gs.pyon the PR brancharctic_platform/integrations/verl/adapter.pyNovaSky-AI/SkyRL:integrations/arctic_rl/tests/client/test_skyrl_verl_compat.py+tests/client/test_rl_cortex_dispatch.py+tests/e2e/test_cortex_transport_smoke.pyarctic_platform/client/UNIFICATION_NOTES.mdSuperseded / closed
#51— verl adapter swap. Closed. Replaced by the env-var override +arctic_platform.rldispatch shim in feat(rl): Cortex-serverless dispatch via arctic_platform.rl (zero integration change) #50; both adapters remain unchanged.