Skip to content

[Core][NIXL] Transfer KV between different P/D KV cache group plans - #60113

Draft
lucamotz wants to merge 35 commits into
vllm-project:mainfrom
lucamotz:rfc-58638-nixl-kv-group-pairs
Draft

lucamotz wants to merge 35 commits into
vllm-project:mainfrom
lucamotz:rfc-58638-nixl-kv-group-pairs

Conversation

@lucamotz

@lucamotz lucamotz commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Overview

NIXL can now transfer KV between P and D whose hybrid KV cache group plans differ, so NixlConnector no longer needs #58207's TP-invariant planning, except for MLA. Motivated by RFC #58638. Stacked on #58207 at e55909cad0, #58860, #59746, #59747 and #59330; only the last four commits are new.

Claims

  • Connectors say whether their peers need matching KV cache groups: requires_matching_kv_cache_groups() defaults to True, so every other connector keeps [Core] Pick hybrid KV cache group size by worst-case padding bytes; add --min-kv-cache-group-layers #58207's planning.
  • NixlConnector pull between different plans is exact: Qwen3.6-35B-A3B-FP8 + DFlash at P TP2 ↔ D TP4 gives bitwise-identical outputs to TP-invariant plans, with 7–28% more KV tokens on D.
  • This also covers plans that differ under TP-invariant planning, which depends on each side's --max-num-batched-tokens, PP and async scheduling.

Validation

pytest tests/v1/core/test_kv_cache_utils.py
pytest tests/v1/kv_connector/unit/test_nixl_connector.py tests/v1/kv_connector/unit/test_nixl_connector_hma.py \
       tests/v1/kv_connector/unit/test_nixl_push_connector.py tests/v1/kv_connector/unit/test_nixl_desc_geometry.py \
       tests/v1/kv_connector/unit/test_tp_mapping.py tests/v1/kv_connector/unit/test_nixl_heartbeat.py \
       tests/v1/kv_connector/unit/test_bidirectional_kv_transfer.py tests/v1/kv_connector/unit/test_handshake_pp_aggregation.py \
       tests/v1/kv_connector/unit/test_nixl_simple_cpu_offload.py tests/v1/kv_connector/unit/test_transfer_topology_sharded.py

On 6× H100, pull with independent plans is bitwise equal to TP-invariant push in both directions and at 262144 tokens. Under load, it shows no TTFT, TPOT or throughput cost against TP-invariant pull beyond run-to-run spread. On 2× DGX Spark, smaller models in pull and push, including D prefix hits and an MTP drafter, are bitwise equal to matching plans too.

CPU tests and byte oracle
  • test_pull_between_different_group_plans (new) builds P and D workers for sliding-window and GDN hybrids whose plans put 2 vs 4 layers in a group, runs the real register_kv_caches, add_remote_agent and pull read path, and replays the recorded NIXL descriptors on CPU tensors: every D head and token holds P's bytes for the same layer, head and token, and nothing else is written.
  • test_kv_transfer_group_planning_is_tp_invariant gets one row per connector; test_layer_handshake_rejects_unsupported_geometry gets a row for a PP push producer whose plan differs.
  • 77126176e8 on main 74c5cbcd7b: the 10 NIXL files pass with 709 tests and test_kv_cache_utils.py with 187, except 3 tests that also fail on main in this environment.
  • A local byte oracle, not part of this PR, replays pull and push across P/D TP pairs up to 8, different plans, block sizes and D prefix hits: 2032 cases are byte-exact and none is silently wrong; the other 238 raise.
GPU: Qwen3.6-35B-A3B-FP8 + DFlash, 6× H100 SXM

07c43a220c on main b1f229fb75. Qwen/Qwen3.6-35B-A3B-FP8 + z-lab/Qwen3.6-35B-A3B-DFlash (7 speculative tokens), TRITON_ATTN, --enforce-eager, greedy, 4 code prompts of 1100–12000 tokens, P TP2 on GPUs 0–1 and D TP4 on 2–5 and the reverse, NCCL_NVLS_ENABLE=0 and VLLM_USE_DEEP_GEMM=0. Each NixlConnector pull with independent plans is compared with a NixlPushConnector run, which keeps TP-invariant plans, on the same image: texts and logprobs are bitwise equal on all 4 prompts, DFlash accepted tokens are equal, and D reads every prompt token but the last over NIXL.

P → D, max_model_len groups P / D, independent vs TP-invariant KV tokens vs TP-invariant
TP2 → TP4, 16384 10 / 8 vs 10 / 10 D +7.3%
TP4 → TP2, 16384 8 / 10 vs 10 / 10 P +7.3%
TP2 → TP4, 262144 17 / 5 vs 10 / 10 P +9.1%, D +27.7%

These runs set --block-size 592 on the TP4 engine, as the new log line suggests. Without it, TP4 picks block 304 and outputs stay bitwise equal, but D's add agent handshake step takes 11–15× longer and NIXL transfers about 6× longer.

Perf A/B: independent vs TP-invariant pull under load, 6× H100 SXM

Arm B is 07c43a220c on main b1f229fb75 (plans 17 / 5); arm A is the same build with "Let pull peers plan KV cache groups independently" reverted (TP-invariant, 10 / 10). P TP2 → D TP4, max_model_len 262144, --block-size 592 on TP4, the flags above. vllm bench serve through the P/D proxy with random 8192-token prompts, run order A B B A, each run restarting both engines; greedy outputs are bitwise equal across all four runs. B − A pools both runs per arm, with a 95% bootstrap CI in ms.

load metric A B B − A 95% CI
1 at a time, 80 requests, 64 output tokens TTFT p50 573.4 ms 564.9 ms −1.5% [−16.1, +0.8]
TTFT p99 623.6 ms 607.6 ms −2.6% [−42.7, +22.3]
TPOT p50 30.38 ms 30.41 ms +0.1% [−9.5, +9.1]
16 at a time, 320 requests, 256 output tokens TTFT p50 1016.0 ms 1013.2 ms −0.3% [−43.7, +16.8]
TTFT p99 3989 ms 4038 ms +1.2% [−509, +578]
TPOT p50 17.21 ms 17.05 ms −0.9% [−3.2, +3.0]
output tok/s 523.4 526.3 +0.6% run spread 3.1% / 1.1%

D's NIXL transfers move the same bytes in both arms; under load their mean time is 77.6 vs 78.9 ms, and post-processing takes about 0.4 ms longer per transfer in B. Eager mode, so absolute numbers are not production numbers.

GB10: smaller models, pull and push, 2× DGX Spark

Build 98132d0d55: this branch's four commits on main d0d6e5f3a2. P and D on one Spark, or the TP2 side across both Sparks; --enforce-eager, greedy. Each case differs between P and D in plan, block size or TP and is compared with the same setup on matching plans and pages, on the same image: texts and logprobs are bitwise equal, and D reads every prompt token but the last over NIXL.

  • Qwen3.5-0.8B (GDN): plans of 3 vs 9 groups, pull and push at TP1 → TP1 and TP1 ↔ TP2, including D prefix hits; with its MTP layer as drafter at TP1 → TP1, accepted tokens are equal.
  • Gemma-4-E2B: pull at TP1 → TP1 and TP2 → TP1, push at TP1 → TP2.
  • Qwen3.6-35B-A3B-FP8 + DFlash: pull at TP1 ↔ TP2, accepted tokens equal.
  • NixlConnector with OffloadingConnector through MultiConnector plans as without a connector and matches NIXL alone on prompts that share no prefix; with shared prefixes, P's CPU offload serves them and logprobs differ slightly.
  • Cross-node transfers ran over RoCE: UCX rc_mlx5, with the reads into GPU memory emulated in software.

Details

  • Hook: replaces the planner's kv_transfer_config is not None check. MultiConnector asks the connectors it wraps; OffloadingConnector with CPUOffloadingSpec and SimpleCPUOffloadConnector return False, since their KV doesn't leave the engine.
  • Group pairs: the handshake, version 15, advertises each region member's KV cache group, KV heads and tokens per page. Where plans differ or the model-wide head mapping doesn't fit every layer, layers transfer per pair of local and remote KV cache groups, in pull and push. Matched plans that the model-wide mapping fits keep today's path.
  • Who plans independently: NixlConnector returns use_mla. NixlPushConnector stays TP-invariant, since a PP push producer routes layers by name. MLA, CSA-linear and PP push producers reject peers with different plans at the handshake.
  • Log: names the --block-size that makes P and D pages divide, once per remote engine.

Not covered: MLA, where a GQA draft under an MLA target stays with #58471 and #59071; DCP; GPUDirect RDMA across nodes. The NIXL compatibility doc isn't updated yet.

The four commits change +700/−51 production lines. The hook can go as a small PR on top of #58207 and the NIXL commits after it, if that is easier to review. No open NIXL PR, and none citing #58638 or #58207, transfers between different KV cache group plans.

AI assistance (Claude Code) was used for the analysis, patch and tests. I reviewed every changed line.


Pull Request Checklist
  • I used vLLM's /pr-checklist skill. (Mandatory for agents, optional for humans).

  • AI assistance was used during the creation of this PR.

  • Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.

  • Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.

  • Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.

  • Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.

🤖 Generated with Claude Code

LucasWilkinson and others added 30 commits September 22, 2026 20:49
A single-layer bucket, typically a speculative drafter layer, drove the
hybrid grouping heuristic to a group size of 1, giving one KV cache group
per layer. Fall back to the multi-layer group size with the least padding
in that case; other models keep their existing grouping.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Replace the min / 1.5x-max heuristic with a search over group sizes from
the smallest bucket (at least 2) to the largest, minimizing padding with
full attention padding weighted over sliding window / mamba padding.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
…se bytes

Bound the hybrid KV cache group size below by min_kv_cache_group_layers
(default 3) and cost padding layers by each spec's max_memory_usage_bytes
instead of fixed full vs. other weights.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Co-authored-by: lucamotz <luca.motz@icloud.com>
Signed-off-by: Lucas Wilkinson <LucasWilkinson@users.noreply.github.com>
KV transfer peers (e.g. P/D) may run different TP sizes, which change
per-rank bytes and so the byte-based group size. With a KV connector,
cost padding by full attention vs. bounded caches instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Tomas Ruiz <tomas.ruiz.te@gmail.com>
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Tomas Ruiz <tomas.ruiz.te@gmail.com>
…arch

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Replace the full-attention-only field comparison with one helper: layers
share a KV cache group if their specs merge, or if they are attention
specs with the same block size, token-slot semantics and page size.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Equal page bytes across different head shapes depend on TP (e.g. Qwen3.5
4x256 target vs 8x128 DFlash drafter match at TP<=4 but not TP8), so KV
transfer peers with different TP sizes could bucket layers differently.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
…e_kv_cache_group_specs

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Page bytes change with TP but the number of pages each layer needs, at
its own page size before unification, doesn't. Replaces the full vs.
bounded split, and needs no special case for Mamba (about one page).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Co-authored-by: Luca Motz <luca.motz@icloud.com>
Signed-off-by: Lucas Wilkinson <LucasWilkinson@users.noreply.github.com>
Co-authored-by: Luca Motz <luca.motz@icloud.com>
Signed-off-by: Lucas Wilkinson <LucasWilkinson@users.noreply.github.com>
kv_postprocess_blksize_on_receive and the tail zeroing in
post_process_device_kv_on_receive index the attention cache as
[B, N, H, D], but since vllm-project#44455 packed K and V into the content dim the
layer views are [B, H, N, 2*D]. With a larger block size on D, the
regroup scrambled every received block on LBHNC, or failed to reshape
with fewer KV heads than block_size_ratio, and the tail zeroing cleared
KV heads instead of the untransferred tokens.

Drop the two permutes and zero along the token dim.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
… views

With HMA, attention groups alias the same KV tensors, but the receive
post-process applied every group's block ids to every attention layer
view. A block received with a block-size ratio was regrouped once per
aliasing group, which scrambles it with more than one KV head per rank
(e.g. gpt-oss).

Post-process each group's blocks only through its own layers' views. The
cached flat list of attention views goes: the caller already skips mamba
groups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
With a block-size ratio, a received block holds the remote sub-blocks
back to back. On a head-major layout (LBHNC, BLHNC) they must be
regrouped per head, but on a token-major layout (LBNHC, BLNHC) they are
already in token order, and the regroup scrambled them.

Regroup only on layouts whose heads are contiguous within a block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
With enable_permute_local_kv, a token-major (LBNHC) decoder receives the
prefiller's head-major (LBHNC) bytes. Both permute helpers still indexed
the cache as a physical [B, N, H, D] tensor, but since vllm-project#44455 it is a
logical [B, H, N, 2*D] view, so they scrambled every received block.

View the received bytes in memory order before reading them as the
remote head-major blocks, and use kv_postprocess_blksize_and_layout_on_receive
at every block-size ratio. kv_postprocess_layout_on_receive was its
ratio-1 case, with the same bug and a 5-D branch dead since vllm-project#51718.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
… sizes

With a block-size ratio, pull front-clips the local (D) sub-blocks to the
remote (P) count, and _apply_prefix_caching then end-trims P to D's
count. Both are right only when the two lists start and end at the same
token, which different block sizes break:
- a partial prefix hit on D, or sliding-window clipping, starts the lists
  at different tokens;
- each side pads its list to its own logical block past the last token.
Every received page then shifts by whole remote blocks. The same end-trim
shifts pages for attention-only models whose kernel blocks match but
whose logical blocks differ.

Drop each side's padding past the request's last token (the
remote_num_tokens P already sends) and pair the lists from there
backwards. The receive post-process zeroes exactly D's padding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
…izes

With DCP, NIXL sets the KV interleave to each side's own logical block
size, so ranks own different tokens once P and D block sizes differ, and
the DCP read path pairs blocks by logical position. A different kernel
block then pulls misaligned tokens and leaves owned ones unwritten, and
equal kernel blocks under different logical blocks fail an assert in
the read.

Reject DCP peers whose logical block size differs at handshake. Different
kernel blocks under an equal logical block size stay supported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
The push connector cannot convert or re-pair blocks for a peer whose
kernel block size or kernel blocks per logical block differ, and D
registers P for notifications without checking either. Depending on the
sizes, D then waits for a WRITE that never comes (P rejects its
handshake, or fails to build the WRITE, after D sent the registration),
or P writes head-sliced or misaligned blocks and D's receive
post-process fails on the block-size ratio assert or on the empty push
remote block ids.

Reject such peers in _validate_remote_parallel_config, which
_nixl_handshake runs for P's and both of D's handshakes (registration
and heartbeat) before registering the peer, so the request fails on D
like any failed handshake. This subsumes the PP-only check in
add_remote_agent and leaves push's block_size_ratio > 1 branch in
_xfer_blocks unreachable, so it is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
P/D decode recomputes the last prompt token. NIXL and Mooncake loaded all N
prompt tokens and the scheduler then rewound one token, after every group was
laid out for position N. A sliding window laid out for N drops the block
holding position N - W whenever N % block_size == block_size - 1, so the
recomputed token attended to the null block; Mamba avoided this only through
a Mamba-specific backoff.

Make that backoff unconditional: the prefiller stops at N - 1 and the decoder
loads N - 1 tokens and computes the last one, as the local prefix cache
already does. Both sides then lay out every group for the position decode
resumes at. Document the contract in get_num_new_matched_tokens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
…s at its end

Bidirectional transfer lets the prefiller pull KV the decoder kept from an
earlier turn. The workers pair the two sides' blocks from the end of the
sequence, so a pull that stops before the decoder's KV ends shifts every
block by one or more positions: a prompt shorter than the decoder's context
(e.g. a regenerated turn), or, now that the prefiller stops one token short,
a prompt the decoder's KV fully covers. Pull only when the decoder's KV ends
before the prefiller's last prompt token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
…ultimodal prompts

Now that every prefiller stops one token short, two prompt shapes that only
Mamba and multi-module MTP models used to hit reach all models:

- A mixed token-id / embeddings prompt kept its embeddings and mask at full
  length, which the worker rejects. Cut all three.
- A prompt ending in a multimodal item leaves the item running past the cut
  prompt. With disable_chunked_mm_input and a prefill lookahead (EAGLE), the
  scheduler rolled back before the item every step and never scheduled it.
  Covering the rest of the prompt now counts as covering the item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
…-side hit

Now that decode loads N - 1 tokens, a decode-side prefix hit of N - 1 tokens
leaves nothing to receive. Push mode then staged no registration, but kept
heartbeating the prefiller, which held its blocks until the decode request
ended, and left an empty recv whose metadata the worker never drops. Stop the
heartbeat so the prefiller reclaims the blocks when its lease lapses, and
finish the remote prefill there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
…t would split

Cutting the last prompt token on the prefiller can split a multimodal item
the prompt ends with. Models parse an item from its whole placeholder:
Qwen3-VL looks up each video frame's end marker and fails to start the
request, and with disable_chunked_mm_input and a lookahead (EAGLE) the
scheduler rolled back before an image running past the cut prompt forever.

Back off to the start of such an item on both sides instead: the prefiller
drops the item, the decoder loads the same prefix and computes the item. The
rule lives in two shared helpers that NIXL and Mooncake both use, which also
cut mixed token-id / embeddings prompts consistently. This replaces the
scheduler change, which is no longer needed since an item never runs past
the cut prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
When a multimodal item starts at the beginning of the prompt and the cut
would split it, there is no prefix both sides can stop at. Falling back to
the full prompt made decode load all N tokens and rewind one, the layout bug
the backoff exists to avoid. Transfer nothing instead: the decoder computes
the whole prompt and the prefiller's own result goes unused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
…ed load

When decode has nothing to load (a decode-side prefix hit of N - 1 tokens, a
1-token prompt, or no safe prefix to cut), Mooncake still sends P an empty
pull so P releases its blocks. The failure path already skipped reporting
such a pull, but the success path reported it as finished_recving for a
request that never waited on a load, tripping the scheduler's assert. Skip it
there too.

Also update the HiSparse NIXL import test for a 1-token prompt, which now
imports nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
Signed-off-by: Luca Motz <luca.motz@icloud.com>
lucamotz and others added 5 commits October 5, 2026 21:34
Lucas Wilkinson's hybrid KV cache group planning, unchanged.

Signed-off-by: Luca Motz <luca.motz@icloud.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With a KV connector configured, vllm-project#58207 plans KV cache groups TP-invariantly,
since the connector's peers (e.g. P and D) may run different TP sizes. Ask
the connector instead: requires_matching_kv_cache_groups() defaults to
True. CPU offloading keeps KV inside the engine, so OffloadingConnector
with CPUOffloadingSpec and SimpleCPUOffloadConnector return False, and
MultiConnector asks the connectors it wraps.

Signed-off-by: Luca Motz <luca.motz@icloud.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…apes

P and D may group the same layers differently, and across TP a layer's KV
heads per rank and tokens per page can change by a different factor than
another layer's (e.g. a target and a drafter across a KV-head replication
boundary). The model-wide head mapping and one engine-wide block size
ratio then don't fit every layer.

The handshake (version 15) advertises each region member's KV cache group,
KV heads and tokens per page. Where the model-wide mapping doesn't fit,
layers transfer in group pairs, split by (local group, remote group,
geometry): each local page receives its own KV heads from the remote ranks
owning them (a read takes each head once, a write every replica), in token
units both pages are multiples of, through prepared lists per remote rank.
Pull and push both use them; a push decode registers the token count both
sides pair from and computes its own pairs to zero what the write leaves
unwritten. Matched plans the model-wide mapping fits keep today's path.

MLA, CSA-linear and PP push layer routing don't use group pairs, so their
peers must group each layer alike, which the handshake now checks. Plans
can differ even when both sides plan TP-invariantly: PP raises the
in-flight tokens the planner costs sliding windows with.

Signed-off-by: Luca Motz <luca.motz@icloud.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Group pairs map different peer plans, so NixlConnector no longer needs
TP-invariant planning, except for MLA. NixlPushConnector keeps it: a PP
producer routes layers by name, without group pairs.

Signed-off-by: Luca Motz <luca.motz@icloud.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a layer's pages on the two engines don't divide each other, group
pairs transfer them in small token units. Log once per remote engine the
--block-size that gives both engines the same block size, and where to
set it.

Signed-off-by: Luca Motz <luca.motz@icloud.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mergify

mergify Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @lucamotz.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

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

kv-cache-manager kv-connector mooncake Mooncake KV-transfer / EC-transfer needs-rebase nixl NIXL KV connector / multi-tiered KV offloading (KVCR)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants