Skip to content

feat(reshard-refit): add no-gather slice-resharding weight refit#496

Open
tanushriya910 wants to merge 2 commits into
mainfrom
tanushriyas/reshard-refit
Open

feat(reshard-refit): add no-gather slice-resharding weight refit#496
tanushriya910 wants to merge 2 commits into
mainfrom
tanushriyas/reshard-refit

Conversation

@tanushriya910

@tanushriya910 tanushriya910 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Adds modelexpress.reshard_refit: an engine-agnostic core for slice-level trainer->inference weight transfer over NIXL/RDMA. It captures which slice of each full source tensor an engine's own weight loader reads (geometry), intersects those against the published source shards (slice_plan), and pulls exactly the needed byte segments (transfer_plan) - no all-gather and no per-model conversion specs.

Includes a NIXL / in-memory transport split behind a Transport protocol, a shard-geometry rendezvous over MxClient, a classic-cudaMalloc MemPool for NIXL-registrable buffers, and an engine-agnostic ReshardReceiver with a vLLM subclass (unquantized meta-twin capture + layerwise-reload / PWAL install, CUDA-graph-safe bare-attr and MLA handling). Adds NixlTransferManager fetch_remote_and_wait for the P2P memory-registration handshake.

##Testing
Implemented and validated bit-exact (max_abs_diff=0) on dense Qwen3-0.6B, fp8 MoE mini-glm, and Qwen3-30B-A3B (128-expert) on nscale B200

Summary by CodeRabbit

  • New Features

    • Added slice-level weight resharding and refitting for inference models.
    • Added support for vLLM model layouts, including quantized models and MLA weights.
    • Added batched RDMA reads for faster weight transfers.
    • Added automatic shard discovery, transfer planning, dtype conversion, and fallback handling.
    • Added CUDA allocation support for registered transfer buffers.
  • Bug Fixes

    • Preserves CUDA-graph tensor addresses during weight updates.
    • Reports parameters that remain uninitialized after installation.
  • Tests

    • Added coverage for geometry capture, slicing, transport, fallback behavior, and end-to-end reconstruction.

Adds modelexpress.reshard_refit: an engine-agnostic core for slice-level
trainer->inference weight transfer over NIXL/RDMA. It captures which slice of
each full source tensor an engine's own weight loader reads (geometry),
intersects those against the published source shards (slice_plan), and pulls
exactly the needed byte segments (transfer_plan) - no all-gather and no
per-model conversion specs.

Includes a NIXL / in-memory transport split behind a Transport protocol, a
shard-geometry rendezvous over MxClient, a classic-cudaMalloc MemPool for
NIXL-registrable buffers, and an engine-agnostic ReshardReceiver with a vLLM
subclass (unquantized meta-twin capture + layerwise-reload / PWAL install,
CUDA-graph-safe bare-attr and MLA handling). Adds NixlTransferManager
fetch_remote_and_wait for the P2P memory-registration handshake.

Signed-off-by: Tanushriya Singh <tanushriyas@nvidia.com>
@copy-pr-bot
copy-pr-bot Bot temporarily deployed to automated-release July 8, 2026 17:47 Inactive
@copy-pr-bot
copy-pr-bot Bot temporarily deployed to automated-release July 8, 2026 17:47 Inactive
@github-actions github-actions Bot added the feat label Jul 8, 2026
@tanushriya910
tanushriya910 marked this pull request as ready for review July 13, 2026 18:09
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds a complete slice-level weight resharding pipeline: geometry capture, shard rendezvous, byte-range planning, CUDA/NIXL transport, generic receiver orchestration, and vLLM-specific installation with quantization and MLA handling.

Changes

Reshard refit pipeline

Layer / File(s) Summary
Geometry capture contracts
modelexpress_client/python/modelexpress/reshard_refit/types.py, .../geometry.py
Records loader view operations and destination layouts using meta-backed LazyWeight tensors, with per-source unsupported fallback handling.
Shard rendezvous and transfer planning
.../rendezvous.py, .../slice_plan.py, .../transfer_plan.py, .../__init__.py
Publishes shard metadata, resolves captured layouts into byte-range reads, and separates direct, conversion, and fallback transfers.
CUDA and NIXL transfer execution
.../cuda_pool.py, .../transport/*, modelexpress_client/python/modelexpress/nixl_transfer.py
Adds classic CUDA allocation, transport abstractions, NIXL session batching, completion polling, and transfer statistics.
Receiver lifecycle and vLLM installation
.../receiver.py, modelexpress_client/python/modelexpress/engines/vllm/reshard.py
Discovers sources, allocates receive buffers, applies updates, performs vLLM reload and quantization processing, preserves graph-bound tensors, and refreshes MLA weights.
Pipeline validation
modelexpress_client/python/tests/test_reshard_refit_*
Tests geometry capture, exact slice reconstruction, batching behavior, dtype validation, and unsupported-source fallback routing.

Estimated code review effort: 5 (Critical) | ~120 minutes

Poem

I’m a rabbit with bytes in my hay,
Pulling neat slices the NIXL way.
Meta twins map every part,
vLLM gets a careful restart.
Hop, hop—new weights are in place!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding no-gather slice-resharding weight refit support.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (5)
modelexpress_client/python/modelexpress/nixl_transfer.py (1)

727-737: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Third near-identical poll/timeout/error loop in this file.

execute_read_batch's wait loop duplicates the same check_xfer_state/timeout/sleep(0.001) pattern already present in receive_from_source (lines 624-638) and receive_dram_into_buffer (lines 829-847). Extracting a small shared _wait_for_xfer(handle, timeout_seconds, label) helper would remove the duplication and unify the (currently slightly different) error messages across all three call sites.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modelexpress_client/python/modelexpress/nixl_transfer.py` around lines 727 -
737, The transfer wait logic is duplicated across execute_read_batch,
receive_from_source, and receive_dram_into_buffer. Add a shared
_wait_for_xfer(handle, timeout_seconds, label) helper that performs polling,
timeout handling, terminal-status checks, and the existing sleep interval, then
replace all three inline loops with calls that provide the appropriate operation
label and preserve each method’s surrounding cleanup behavior.
modelexpress_client/python/modelexpress/reshard_refit/slice_plan.py (1)

227-236: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add strict=True to the zip() calls over shard geometry.

intersect (here), and the zip()s in paired_runs (L250-251) and plan_pull's shard loop (L299), assume box_a/box_b/sh.shard_offset/sh.shape are always rank-consistent. That data is decoded from wire/JSON shard metadata (rendezvous.py), not locally trusted, so a malformed shard entry would silently truncate instead of raising a clear error.

Proposed fix (apply to all 4 sites)
-    for (a0, a1), (b0, b1) in zip(box_a, box_b):
+    for (a0, a1), (b0, b1) in zip(box_a, box_b, strict=True):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modelexpress_client/python/modelexpress/reshard_refit/slice_plan.py` around
lines 227 - 236, Update all four shard-geometry zip calls in intersect,
paired_runs, and plan_pull to use strict=True, ensuring rank-mismatched metadata
raises an error instead of silently truncating. Preserve the existing
intersection and planning behavior for rank-consistent inputs.

Source: Linters/SAST tools

modelexpress_client/python/modelexpress/reshard_refit/transport/nixl.py (1)

58-67: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Inconsistent fail-fast policy: session_to_agent raises, session_to_device silently defaults.

Missing agent mapping raises KeyError (Line 65-66), but a missing device mapping silently falls back to device 0 (Line 67). A wrong/missing device id causes a READ from the wrong GPU memory - silent data corruption is worse than a crash. rendezvous.build_sources currently populates both maps together for every shard, so this shouldn't trigger via that path today, but the transport doesn't enforce that invariant itself.

Proposed fix
-            device_id = self._session_to_device.get(session, 0)
+            if session not in self._session_to_device:
+                raise KeyError(f"no remote device id registered for session {session!r}")
+            device_id = self._session_to_device[session]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modelexpress_client/python/modelexpress/reshard_refit/transport/nixl.py`
around lines 58 - 67, Update read in the session-group handling to require a
device mapping for every session, matching the existing fail-fast behavior for
_session_to_agent. Remove the default device ID fallback from
_session_to_device.get and raise a clear error when the mapping is absent, while
preserving the existing mapped-device path.
modelexpress_client/python/modelexpress/reshard_refit/rendezvous.py (1)

53-62: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Narrow the caught exception in _mx_version.

Catching bare Exception masks bugs unrelated to a missing package (e.g. a corrupt metadata read), and both sides silently falling back to different values only manifests as a confusing rendezvous timeout later. importlib.metadata.version raises PackageNotFoundError specifically when the package isn't installed - catch that instead.

Proposed fix
     from importlib.metadata import version as pkg_version
 
     try:
         return pkg_version("modelexpress")
-    except Exception:
+    except PackageNotFoundError:
         return "0.0.0"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modelexpress_client/python/modelexpress/reshard_refit/rendezvous.py` around
lines 53 - 62, Update `_mx_version` to catch only `PackageNotFoundError` from
`importlib.metadata` when the `modelexpress` package is unavailable, while
preserving the `"0.0.0"` fallback for that case. Allow other metadata errors to
propagate instead of masking them.

Source: Linters/SAST tools

modelexpress_client/python/modelexpress/reshard_refit/transfer_plan.py (1)

91-112: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use keyword args for RecordedCopy Prefer keywords here (and in the matching call in geometry.py) so a future RecordedCopy field change can’t silently swap arguments.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modelexpress_client/python/modelexpress/reshard_refit/transfer_plan.py`
around lines 91 - 112, Update the staging-buffer construction in the reshard
planning flow to instantiate RecordedCopy with keyword arguments, explicitly
naming each field currently passed positionally. Apply the same keyword-argument
change to the matching RecordedCopy call in geometry.py, without changing values
or behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modelexpress_client/python/modelexpress/engines/vllm/reshard.py`:
- Around line 172-188: Update the bare-attribute restore loop in the snapshot
restoration flow to log an error whenever the current tensor and boot tensor
have mismatched shapes or dtypes and the copy is skipped. Include the
attribute/module identity and both tensor metadata in the diagnostic, while
preserving the existing copy-and-reattach behavior for matching tensors and
reattachment behavior afterward.

In `@modelexpress_client/python/modelexpress/nixl_transfer.py`:
- Around line 684-699: The diagnostic block in execute_read_batch currently
performs metadata checks and info-level formatting on every read. Gate the
entire check_remote_metadata and sample logging block behind
logger.isEnabledFor(logging.DEBUG), switch the output to logger.debug, and
narrow the exception handling where possible or explicitly annotate the
intentional broad catch while preserving transfer execution if diagnostics fail.

In `@modelexpress_client/python/modelexpress/reshard_refit/receiver.py`:
- Around line 165-274: The receiver currently only logs plan.fallback entries,
leaving unsupported parameters uninitialized because the promised
full-pull/loader path is never invoked. Update the preparation or update flow
around _prepare and update_weights to execute the existing full-pull/loader
mechanism for every fallback source, while preserving RDMA handling for
supported segments and ensuring fallback parameters are installed before
returning from an update.

In `@modelexpress_client/python/tests/test_reshard_refit_nixl_transport.py`:
- Line 51: Rename the unused unpacked variable a_agent to _a_agent in the
by_agent["trainer-agent-A"] assignment, preserving the existing tuple unpacking
and all other values unchanged.

---

Nitpick comments:
In `@modelexpress_client/python/modelexpress/nixl_transfer.py`:
- Around line 727-737: The transfer wait logic is duplicated across
execute_read_batch, receive_from_source, and receive_dram_into_buffer. Add a
shared _wait_for_xfer(handle, timeout_seconds, label) helper that performs
polling, timeout handling, terminal-status checks, and the existing sleep
interval, then replace all three inline loops with calls that provide the
appropriate operation label and preserve each method’s surrounding cleanup
behavior.

In `@modelexpress_client/python/modelexpress/reshard_refit/rendezvous.py`:
- Around line 53-62: Update `_mx_version` to catch only `PackageNotFoundError`
from `importlib.metadata` when the `modelexpress` package is unavailable, while
preserving the `"0.0.0"` fallback for that case. Allow other metadata errors to
propagate instead of masking them.

In `@modelexpress_client/python/modelexpress/reshard_refit/slice_plan.py`:
- Around line 227-236: Update all four shard-geometry zip calls in intersect,
paired_runs, and plan_pull to use strict=True, ensuring rank-mismatched metadata
raises an error instead of silently truncating. Preserve the existing
intersection and planning behavior for rank-consistent inputs.

In `@modelexpress_client/python/modelexpress/reshard_refit/transfer_plan.py`:
- Around line 91-112: Update the staging-buffer construction in the reshard
planning flow to instantiate RecordedCopy with keyword arguments, explicitly
naming each field currently passed positionally. Apply the same keyword-argument
change to the matching RecordedCopy call in geometry.py, without changing values
or behavior.

In `@modelexpress_client/python/modelexpress/reshard_refit/transport/nixl.py`:
- Around line 58-67: Update read in the session-group handling to require a
device mapping for every session, matching the existing fail-fast behavior for
_session_to_agent. Remove the default device ID fallback from
_session_to_device.get and raise a clear error when the mapping is absent, while
preserving the existing mapped-device path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8ab1562d-568a-4704-83df-09f1656fe659

📥 Commits

Reviewing files that changed from the base of the PR and between 850da94 and e4d1a57.

📒 Files selected for processing (17)
  • modelexpress_client/python/modelexpress/engines/vllm/reshard.py
  • modelexpress_client/python/modelexpress/nixl_transfer.py
  • modelexpress_client/python/modelexpress/reshard_refit/__init__.py
  • modelexpress_client/python/modelexpress/reshard_refit/cuda_pool.py
  • modelexpress_client/python/modelexpress/reshard_refit/geometry.py
  • modelexpress_client/python/modelexpress/reshard_refit/receiver.py
  • modelexpress_client/python/modelexpress/reshard_refit/rendezvous.py
  • modelexpress_client/python/modelexpress/reshard_refit/slice_plan.py
  • modelexpress_client/python/modelexpress/reshard_refit/transfer_plan.py
  • modelexpress_client/python/modelexpress/reshard_refit/transport/__init__.py
  • modelexpress_client/python/modelexpress/reshard_refit/transport/base.py
  • modelexpress_client/python/modelexpress/reshard_refit/transport/nixl.py
  • modelexpress_client/python/modelexpress/reshard_refit/types.py
  • modelexpress_client/python/tests/test_reshard_refit_geometry.py
  • modelexpress_client/python/tests/test_reshard_refit_nixl_transport.py
  • modelexpress_client/python/tests/test_reshard_refit_slice_plan.py
  • modelexpress_client/python/tests/test_reshard_refit_transfer.py

Comment thread modelexpress_client/python/modelexpress/engines/vllm/reshard.py
Comment thread modelexpress_client/python/modelexpress/nixl_transfer.py Outdated
Comment thread modelexpress_client/python/modelexpress/reshard_refit/receiver.py
Comment thread modelexpress_client/python/tests/test_reshard_refit_nixl_transport.py Outdated
- reshard.py: log (not silently skip) bare-attr restores whose shape/dtype
  changed across a refit, so stale re-attachment is visible
- nixl_transfer.py: gate the per-READ diagnostic behind DEBUG (debug level)
  so steady-state refits don't pay check_remote_metadata + formatting; annotate
  the intentional broad except (BLE001)
- receiver.py: raise UnsupportedReshard on non-empty plan.fallback instead of
  only warning - fallback params are never pulled, so warning-and-continuing
  silently served stale weights (full-pull path is a TODO)
- test: rename unused unpack a_agent -> _a_agent (RUF059)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant