[WIP][multi-lora] rewrite: tinker-compatible multi-LoRA backend - #2775
Draft
yushengsu-thu wants to merge 22 commits into
Draft
[WIP][multi-lora] rewrite: tinker-compatible multi-LoRA backend#2775yushengsu-thu wants to merge 22 commits into
yushengsu-thu wants to merge 22 commits into
Conversation
yushengsu-thu
commented
Aug 29, 2026
yushengsu-thu
force-pushed
the
new2-multi-lora-tinker
branch
2 times, most recently
from
August 29, 2026 22:09
5812cd7 to
16d544e
Compare
Empty commit so a draft PR against main can open before the first implementation commit lands.
yushengsu-thu
force-pushed
the
new2-multi-lora-tinker
branch
from
August 30, 2026 06:33
e1abe87 to
3d104c8
Compare
Multi-LoRA v2 needs the MultiLoRA fixes on Megatron-Bridge @bridge (the pinned 7f0fb345 revision predates them; moving ref, so rebuild with --no-cache).
yushengsu-thu
force-pushed
the
new2-multi-lora-tinker
branch
from
August 30, 2026 06:41
ab443ed to
0961dad
Compare
AdapterIdentity (AdapterRef's name/slot plus the never-reused registration_id) and the committed ServingRef give registry, rollout, and trainer one exact-identity shape without a new datatype module; the two optimizer step policy literals live with their implementer so tinker's request-driven AdamW-on-sum and E2E's scheduled mean share one dispatch vocabulary.
yushengsu-thu
commented
Aug 30, 2026
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class AdapterIdentity: |
Collaborator
Author
There was a problem hiding this comment.
maybe change name to LoraIdentity
Collaborator
Author
There was a problem hiding this comment.
In all multi-lora related files (including e2e or tinker) class, function, var with adapter prefix or postfix need to be changed to lora
install_adapter_slot/release_adapter_slot carry only the rank-local mechanism (optional state_dict load-then-init, seed forked off the global RNG stream, clear plus optimizer/grad/scheduler cleanup) so the tinker frontend can drive slots without inheriting checkpoint auto-resume, controller RPCs, or the final-save policy, which stay in the _register/_deregister e2e wrappers unchanged.
yushengsu-thu
commented
Aug 30, 2026
A batch may carry request_loss_fn (+config) to pick the loss per call; without it dispatch is unchanged. Rejected under recompute_loss_function because checkpoint reruns the loss fn in backward.
yushengsu-thu
force-pushed
the
new2-multi-lora-tinker
branch
from
August 30, 2026 07:22
912823b to
a56c6b8
Compare
forward_with_request_loss reuses the forward_only pipeline (new default-off extra_batch_keys/include_batch_in_f params) and a collector that returns detached per-sample logprobs in input order plus the request-scoped loss.
forward_backward_with_request_loss runs train() with optimizer=None so grads accumulate under keep-grads; request loss keys ride the batch via the new extra_loss_batch param and per-step losses come back via loss_sink.
step_adapter_slots now takes per-slot grad scale and clip (E2E passes 1/batch + args.clip_grad, tinker passes 1.0 + AdamParams.grad_clip_norm) plus an opt-in finite vote that zeroes and skips a non-finite slot; apply_adam_params writes per-call AdamW hyperparameters, and slots without a scheduler are skipped.
Each rank saves its owned share of one slot's fp32 masters, Adam moments, and step clocks under slot-neutral param keys (per-global-rank files, since LayerWise scatters whole params across DP); full load restores them and syncs master to model instead of reload_model_params. save_multi_lora_checkpoints gains an opt-in include_optimizer_state.
export_adapter_slot gathers one slot's HF-named adapter tensors sliced to the real rank as an immutable CPU snapshot (no push, no ServingRef commit); the checkpoint HF export now goes through the same function.
One preflight/execute pair per backend verb on MegatronTrainRayActor, each validating the exact slot binding first, delegating to the existing primitives; the pure validators live in utils/multi_lora.
The GPU gate showed init_adapter_slot only sets rank/alpha masks while weights come from the previous clear's unseeded re-init, so a seeded create was not deterministic; the seeded path now clears and re-inits under a scope that also reseeds the megatron CUDA-RNG tracker.
yushengsu-thu
commented
Aug 30, 2026
install_adapter_slot now passes seed straight to the bridge's init_adapter_slot (radixark/Megatron-Bridge#34), which owns LoRA weight init and the RNG-tracker reseeding; the local _seeded_rng_scope dance is gone. A seed is ignored when loading a state_dict.
yushengsu-thu
force-pushed
the
new2-multi-lora-tinker
branch
from
August 30, 2026 18:15
a121c17 to
f5a0666
Compare
One fast-gpu test drives every rank-local handler on a real tiny MultiLoRALinear + per-slot optimizer: create (seeded), forward/FB delegation, optim_step, save_state, load_state with and without optimizer, export_adapter, and release.
yushengsu-thu
commented
Aug 30, 2026
Comment on lines
+189
to
+197
| for slot, scale in step_scales.items(): | ||
| children = _slot_children(optimizer, slot) | ||
| # Copy accumulated main_grads into the owned masters' grads, then scale the sum to the adapter-batch mean. | ||
| # Copy accumulated main_grads into the owned masters' grads, then apply the caller's scale to the sum. | ||
| for child in children: | ||
| child.prepare_grads() | ||
| for main_param in child.get_parameters(): | ||
| if main_param.grad is not None: | ||
| main_param.grad.mul_(1.0 / batch_size) | ||
| if scale != 1.0: | ||
| for main_param in child.get_parameters(): | ||
| if main_param.grad is not None: | ||
| main_param.grad.mul_(scale) |
Collaborator
Author
There was a problem hiding this comment.
note: check if need any optimization in computing on later
Collaborator
Author
|
(Above) Backend, Trainer:
|
yushengsu-thu
force-pushed
the
new2-multi-lora-tinker
branch
from
August 31, 2026 00:30
b05765e to
201f09d
Compare
yushengsu-thu
commented
Aug 31, 2026
Serving-only consumers need /generate without Sample, reward, or GenerateState coupling; generate() keeps admission and sample mutation and now delegates payload assembly, POST, and logprob parsing.
yushengsu-thu
force-pushed
the
new2-multi-lora-tinker
branch
6 times, most recently
from
August 31, 2026 04:49
115ed08 to
43f9e0f
Compare
Per-adapter sources become RolloutDataSourceWithBuffer so recycled groups actually return, reconcile rebuilds a source when its name is re-registered so a new tenant never reads the old tenant's data, and get_samples takes an eligibility filter for admission-gated producers.
yushengsu-thu
force-pushed
the
new2-multi-lora-tinker
branch
2 times, most recently
from
August 31, 2026 06:31
806c765 to
061763a
Compare
get_groups now pops an adapter only as one complete batch under its exact ServingRef and blocks it until publication advances the version; the producer caps buffered+inflight groups at the open batch quota. The old partial-pop unit tests are removed in favor of a GPU CI rollout-to-handoff E2E (test_multi_lora_rollout.py) driving real SGLang multi-LoRA generation through admission to the handoff.
# Conflicts: # miles/backends/megatron_utils/model.py
yushengsu-thu
force-pushed
the
new2-multi-lora-tinker
branch
from
August 31, 2026 07:17
061763a to
b8dd437
Compare
Collaborator
Author
|
(Above) rollout (e2e):
|
yushengsu-thu
force-pushed
the
new2-multi-lora-tinker
branch
3 times, most recently
from
August 31, 2026 17:44
bb0aca3 to
c9f58d3
Compare
Re-add the tinker==0.26.1 Dockerfile pin and add ensure_tinker_runtime(), failing fast on Python < 3.11 or a missing or mismatched SDK without importing tinker.
yushengsu-thu
force-pushed
the
new2-multi-lora-tinker
branch
from
August 31, 2026 19:40
47bbcce to
4e0efcc
Compare
Add --multi-lora-frontend with an e2e/tinker validation split, a serving-only RolloutManager branch that skips dataset, rollout-fn, reward, and eval wiring, an epoch-math guard, and a launcher branch that boots the SGLang fleet and hands the router URL to the controller. Per-request sampling stays off the RolloutManager per the direct-to-SGLang design.
Serve the unmodified tinker SDK sampling plane from the controller process: sessions, long-poll futures, dedupe, two-level admission, and proto sample terminals in miles/tinker, dispatched straight to SGLang through a one-shot post seam on the neutral endpoint. Generalize MultiLoRABackend aborts to exact-rid/prefix and mount the routes on the existing control-plane app.
Add tinker/datum_batch.py turning ForwardBackwardInput wire dicts into the existing rollout_data keys with a plain loss spec and (request_id, row) ownership sideband, and tinker/losses.py validating loss names, configs, and required inputs; cispo and dro reject as unsupported.
Collaborator
Author
|
(Above) Rollout (tinker): - most effort on miles/tinker
|
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.
Clean and refactor now (#2365): split into