Skip to content

feat(dynamo): support python based dynamo vLLM workers administration - #3629

Open
biswapanda wants to merge 18 commits into
PrimeIntellect-ai:mainfrom
biswapanda:feat/dynamo-vllm-python-admin
Open

biswapanda wants to merge 18 commits into
PrimeIntellect-ai:mainfrom
biswapanda:feat/dynamo-vllm-python-admin

Conversation

@biswapanda

@biswapanda biswapanda commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Discover and administer Python dynamo.vllm workers through their advertised engine routes while preserving the collective-RPC sidecar contract.
  • Handle tensor-parallel world sizes, including one successful collective-RPC result per rank, and map NCCL initialization, pause, update, and resume operations to Python workers.
  • Manage versioned Dynamo LoRA adapters, retention for the off-policy window, transient administration retries, and rollback without forwarding frontend credentials to Python workers.
  • Select renderer-based evaluation only for Dynamo and forward the policy-staleness budget to its admin plane.
  • Provide Dynamo examples under examples/extra/dynamo/basic/ and examples/extra/dynamo/vlm/nemotron-3.5-super/, with focused Dynamo administration tests.

Why

Python Dynamo workers advertise a different administration contract from the existing collective-RPC sidecar. Prime-RL must discover the correct worker topology and routes, translate weight-update operations, and use Dynamo's versioned adapter names consistently. Dynamo generation also needs matching renderer-based evaluation requests and adapter retention aligned with the off-policy window.

Dependency and scope

Shared support #3735 must merge first. Both PRs target main because an upstream PR cannot use a fork-only branch as its base. This PR intentionally depends on the shared renderer and LoRA interface hooks; its standalone LoRA path is incomplete until the prerequisite lands.

Backend-independent configuration, routed-expert completeness checks (including missing_routed_expert_branches), rollout rejection handling, generic adapter-model propagation, and their tests have moved to that prerequisite. The remaining production diff is limited to Dynamo administration, Dynamo factory wiring, and Dynamo-specific orchestrator selection. This PR retains the tensor-parallel runtime behavior previously developed in #3618.

Validation

  • The composed shared-plus-Dynamo stack passed 197 focused tests; the shared watcher regression also passed in the SSH dev-pod runtime with GPU visibility disabled.
  • The composed production tree exactly matches the pre-split integration merged with latest main (7caf94fa4); only a native LoRA regression test was added.
  • Ruff and diff whitespace checks passed.
  • Prior Qwen3-0.6B MathEnv NCCL validation completed three updates with mismatch KL 0.0004, 0.0004, and 0.0003.
  • The September 30 composed Nemotron integration completed separate three-step AutomationBench text and color-codeword image smoke runs with filesystem LoRA and routed-expert replay. Those runs used additional Nemotron PRs and validation-only fixes; they are not standalone acceptance of this extracted head or evidence of convergence.
  • No fresh GPU end-to-end run was performed for this scope-only extraction. The broader local recipe-loading suite still encountered environment-plugin/configuration failures and is not counted as passing.

@biswapanda
biswapanda force-pushed the feat/dynamo-vllm-python-admin branch from d8ca75c to 9c4765c Compare September 22, 2026 20:56
@biswapanda biswapanda changed the title feat(dynamo): support Python vLLM RL administration feat(dynamo): support python based dynamo vLLM workers administration Sep 22, 2026
@biswapanda
biswapanda marked this pull request as ready for review September 22, 2026 23:15
@biswapanda
biswapanda force-pushed the feat/dynamo-vllm-python-admin branch from e74ab66 to 12b672a Compare September 30, 2026 05:00
@biswapanda
biswapanda force-pushed the feat/dynamo-vllm-python-admin branch from 12b672a to e10f001 Compare September 30, 2026 05:03
samsja added a commit that referenced this pull request Oct 10, 2026
Replaces the three switches `model.fsdp_cpu_offload`,
`model.optim_cpu_offload` and `model.full_offload` with one field:

```toml
[model]
offload = "optimizer"   # default; also "full", "none"
```

This applies to both the RL trainer and SFT. The `fsdp` switch is not
migrated: the mode is removed outright (rationale below). The
`[model.full_offload] numa_bind` knob is dropped: no recipe, deployment,
template or doc sets it. Full offload now always pins each rank's CPUs
to its GPU's NUMA node. That was already the default. Ranks whose GPU
reports no NUMA node still skip binding with a warning. On hosts without
GPU sysfs topology (`/sys/bus/pci/devices/<bus>/numa_node`), binding is
no longer optional: full offload fails there instead of being switched
off.

Why: the three switches could only be used one at a time, enforced by
`cpu_offload_mutual_exclusion`. Because `optim_cpu_offload` defaulted to
`true`, enabling full offload also meant writing `optim_cpu_offload =
false`, otherwise validation failed (see the old
`configs/advanced/deepseek-v4-flash/sft.toml` comment). With one value
the conflicting combinations cannot be expressed, so that validator is
deleted. The default behaviour does not change: optimizer-state offload
stays on.

Why drop `fsdp`: the mode was the stock torch FSDP2 `CPUOffloadPolicy`
with no in-tree config, example, or test user; Muon rejected it and it
broke NCCL weight broadcast. It was kept only for long-context runs
whose fp32 masters/moments no longer fit on GPU (the reason it was
restored in #3052); the `full` mode added in #3234 keeps FP32 masters,
moments, and gradients in host RAM, covering that memory pressure
instead. Gone with it: the `CPUOffloadPolicy` selection in `setup_fsdp`,
CPU-side model init in `setup_model`/`load_dcp_from_hf`,
`_move_buffers_to_cuda`, and the fsdp leg of the Gloo special case.

Changes:
- `ModelConfig.offload: Literal["none", "optimizer", "full"] =
"optimizer"` replaces the three fields.
`OptimizerInBackwardOffloadConfig` (and with it `numa_bind`) and its
bool-normalizing `BeforeValidator` are gone.
- Validators in `TrainerConfig` and `SFTConfig`:
- full offload accepts only AdamW or SignSGD and disables clipping, now
reading `offload == "full"`;
- the `Muon optimizer does not support FSDP CPU offload` validators are
deleted with the mode.
- Runtime:
- `setup_optimizer(..., offload=...)` replaces `cpu_offload` +
`full_offload_config`, and its "both enabled" check is gone.
- `FullCPUOffloadOptimizer` drops its unused `offload_config` argument.
- `setup_full_cpu_optimizer_offload()` takes no arguments and always
binds.
- `model.py`: FSDP always uses the default `OffloadPolicy()`; CPU-side
model init in `setup_model`/`load_dcp_from_hf` and
`_move_buffers_to_cuda` are gone.
- Tomls: `glm-4.5-air/swe-budget.toml` and
`ci/integration/reverse-text-sft/full-offload-resume.toml` → `offload =
"full"`; `glm-5.3/sft/h200/16k-12-node.toml` → `offload = "none"`;
`glm-5.3/sft/h200/base.toml` → `offload = "optimizer"`. Also updated
`docs/scaling.md`, the `deepseek-v4-flash/sft.toml` comment, and
`skills/training/start-run/SKILL.md`, which documented the removed
fields.

Breaking: the old keys now fail with `extra_forbidden`, and `offload =
"fsdp"` fails the `Literal`. There is no shim.

Interaction with #3849 (one trainer-common config base): #3849 moves the
full-offload and Muon/FSDP validators from `TrainerConfig`/`SFTConfig`
onto `BaseTrainerConfig`. This PR edits the full-offload validator's
condition and deletes the Muon/FSDP one, so the conflict is textual
only: keep #3849's single copy of the full-offload validators with
`self.model.offload == "full"` and drop its Muon/FSDP copy. Whichever
lands second takes a small rebase.

Other open PRs that set or read the old keys need a rebase:
- tomls: #3883, #3879, #3766, #3765, #3729, #3629, #3627, #3616, #3544
- code: #3885 (`optim_cpu_offload`), #3739 (`full_offload` validators),
#3724, #3719, #3699, #3610, #3487 (`fsdp_cpu_offload` / `enable_gloo`)

Test plan
- `pytest tests/unit/orchestrator tests/unit/test_configs.py
tests/unit/train/test_cpu_adam.py tests/unit/train/test_state_offload.py
-m "not gpu"`: 336 passed, 1 skipped (Qwen3-VL snapshot not cached on
this host), 4 deselected (GPU; this host has no GPU). The existing
offload config tests are updated to the new field. `test_load_configs`
covers the 4 edited tomls.
- `cli(TrainerConfig|SFTConfig, ...)`: `--model.offload full|none` and
the default (`optimizer`) resolve as expected; `optim_cpu_offload` and
`offload = "fsdp"` are rejected.
- `import prime_rl.trainer.rl.train, prime_rl.trainer.sft.train` works.
- `ruff check` and `ruff format --check` on the changed Python files:
clean.
- Not run: GPU or multi-node training with `offload = "full"`.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Breaking TOML/API surface touches distributed training memory paths
(FSDP offload, optimizer-in-backward, Gloo); behavior should match prior
modes when mapped correctly, but mis-migrated configs will fail at parse
time.
> 
> **Overview**
> **Breaking config change:** `model.fsdp_cpu_offload`,
`model.optim_cpu_offload`, and `model.full_offload` are replaced by a
single **`model.offload`** setting (`"optimizer"` default, or `"full"`,
`"fsdp"`, `"none"`). RL and SFT share the same field; example TOMLs and
scaling docs are updated accordingly.
> 
> The mutual-exclusion validator between the old flags is removed
because only one mode can be expressed. **`[model.full_offload]` /
`numa_bind` is dropped** — `"full"` always runs NUMA binding and CPU
optimizer thread setup via parameterless
`setup_full_cpu_optimizer_offload()`. Runtime paths (`setup_optimizer`,
FSDP CPU offload in `model.py`, Gloo when offload is `"full"` or
`"fsdp"`) key off the new enum; `FullCPUOffloadOptimizer` no longer
takes an offload config object.
> 
> Default training behavior is unchanged: optimizer-state CPU offload
remains the default (`"optimizer"`). Old TOML keys are not aliased and
should fail validation.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
35da73a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: sami jaghouar <sami.jaghouar@gmail.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant