[TRTLLM-12498][feat] Add support for beam search in disaggregated serving - #14876
Conversation
📝 WalkthroughWalkthroughThis PR enables beam-search sampling across disaggregated context and generation phases by lifting beam-width restrictions, supporting flexible block-ID shapes, propagating first-generation cumulative log probabilities, and seeding generation-side beam state from context-side history. ChangesBeam-search disaggregated generation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
tensorrt_llm/_torch/pyexecutor/resource_manager.py (1)
1196-1201:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix return type for beam-aware get_batch_cache_indices
Whenbeam_width > 1,get_batch_cache_indicesreturns a per-request list of beams (List[List[List[int]]]), but the signature still declaresList[List[int]].♻️ Proposed typing fix
- ) -> List[List[int]]: + ) -> List[List[int]] | List[List[List[int]]]:Consider using
@overloadas a follow-up to precisely type thebeam_width=1vsbeam_width>1cases.🤖 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 `@tensorrt_llm/_torch/pyexecutor/resource_manager.py` around lines 1196 - 1201, The return type annotation for get_batch_cache_indices is incorrect for beam-aware calls: when beam_width > 1 the function returns a nested per-request-per-beam structure (List[List[List[int]]]) but the signature declares List[List[int]]; update the function's return type to reflect the beam-aware shape (or add `@overload` signatures to distinguish beam_width==1 vs >1) and adjust any related type imports so callers and linters correctly see the List[List[List[int]]] result for multi-beam cases; reference the get_batch_cache_indices function and its beam_width parameter when making the change.tensorrt_llm/_torch/pyexecutor/py_executor.py (1)
3575-3590:⚠️ Potential issue | 🟠 Major | ⚡ Quick winValidate
first_gen_tokensbefore indexing by beam.Line 3589 assumes the handoff always carries one token per beam, but this path never checks that
context_phase_params.first_gen_tokensis present andlen(...) == beam_width. A truncated payload will raise here and abort the executor loop instead of failing just this request.Suggested guard
first_gen_tokens = req.context_phase_params.first_gen_tokens ctx_draft_tokens = req.context_phase_params.draft_tokens req.py_draft_tokens = [] if ctx_draft_tokens is None else ctx_draft_tokens beam_width = req.sampling_config.beam_width + if (first_gen_tokens is None + or len(first_gen_tokens) != beam_width): + self._handle_errors( + "Invalid first_gen_tokens length for disagg beam " + f"request {req.py_request_id}: " + f"{0 if first_gen_tokens is None else len(first_gen_tokens)} != {beam_width}", + requests=[req], + charge_budget=False, + ) + continue logger.info(🤖 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 `@tensorrt_llm/_torch/pyexecutor/py_executor.py` around lines 3575 - 3590, The code assumes req.context_phase_params.first_gen_tokens contains exactly beam_width tokens before looping; add a guard that checks first_gen_tokens is not None and len(first_gen_tokens) >= beam_width (or == beam_width) before indexing. If the check fails, log a request-scoped warning via logger including req.py_request_id and ctx_request_id, mark or fail only this request (do not raise), and skip the per-beam add_new_token/seed call (or supply safe defaults) so the executor loop continues; update the block referencing first_gen_tokens, req.add_new_token, beam_width, and self._seed_disagg_beam_cum_log_probs accordingly.
🤖 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 `@tensorrt_llm/_torch/disaggregation/native/transfer.py`:
- Around line 64-69: The current _block_ids_debug_summary eagerly converts large
numpy arrays to Python lists (arr.tolist()) causing CPU/alloc and log bloat on
the hot path; change the logging payload to include only lightweight metadata
(shape and count) by default and only produce the full block id lists when the
logger is in DEBUG mode: update _block_ids_debug_summary to return shapes and
lengths for each layer_group and move the arr.tolist() conversion behind a debug
check (e.g., logger.isEnabledFor(logging.DEBUG)) so the costly conversion is
skipped in normal INFO logs; apply the same pattern where full block-id dumps
are used elsewhere (references: the other debug-summary-like blocks and
functions around the noted areas and any uses that call
_block_ids_debug_summary) so full lists are generated only when debug logging is
enabled.
In `@tensorrt_llm/_torch/disaggregation/transceiver.py`:
- Around line 223-233: The info log inside _create_kv_slice (the
"create_kv_slice final block_ids" message) currently materializes block_ids with
block_ids.tolist() on the hot path; change it to avoid eager full-array
conversion by removing block_ids.tolist() from the info-level message (log only
metadata such as block_ids.shape and count/length) and, if full contents are
ever needed, emit them at debug level guarded by
logger.isEnabledFor(logging.DEBUG) (or similar) so the array is only converted
when debug logging is enabled.
In `@tensorrt_llm/_torch/pyexecutor/py_executor.py`:
- Around line 3606-3619: If seeding the beam-state fails (missing seq_slot,
sampler.store.beam_search_store, or malformed first_gen_cum_log_probs) do not
simply log and return; instead mark the request as failed (set its status to
DISAGG_TRANS_ERROR or invoke the request-failure path) and abort further
processing so partially-initialized beam state cannot be used. Update the
branches that currently log+return (check occurrences around seq_slot,
beam_search_store, and the first_gen_cum_log_probs handling in the
py_executor.py routine) to call the request failure routine or set req.status =
DISAGG_TRANS_ERROR and ensure the function exits immediately after that change.
In `@tensorrt_llm/_torch/pyexecutor/resource_manager.py`:
- Around line 1212-1215: The loop that zips request_ids with the result of
self.impl.get_batch_cache_block_ids() (in resource_manager.py) can silently
truncate if counts differ; add an explicit guard right after calling
get_batch_cache_block_ids() to compare len(result) with len(request_ids) and
raise a clear RuntimeError or AssertionError (including the mismatched lengths
and identifying info like request_ids[:5]) if they differ; apply the same length
check/early-fail before the second zip/loop around the code near the other
occurrence (around line 1247) so any request→block mapping mismatch fails fast
and surfaces the bug.
In `@tensorrt_llm/_torch/pyexecutor/sampler.py`:
- Around line 119-123: The helper function _debug_tensor_slice (and all other
newly introduced helper functions whose names begin with _debug_ in this module)
lack explicit return type annotations; update each function signature to include
an explicit return type (use -> None for helpers that do not return) so they
comply with the repo typing rule—ensure you modify _debug_tensor_slice and the
other _debug_* helpers referenced in the review to add the -> None return
annotation.
- Around line 3324-3336: In TorchSampler._finalize_beam the variable
request_tokens_after is constructed but never logged; update the subsequent
logger.info call (in the _finalize_beam method) to include request_tokens_after
(e.g., as part of the formatted message or an extra kwarg) so the computed
per-beam token info is emitted; ensure you serialize/format request_tokens_after
concisely (repr or json-safe) to avoid huge logs.
- Around line 2898-2908: The verbose tensor-to-host debug dumps in
TorchSampler._prepare_beam_search are causing eager CUDA syncs and must be
gated: change the logger.info calls that call helpers like _debug_tensor_slice
and _debug_cache_indirection_prefix to be executed only when debug logging (or a
sampler debug flag) is enabled — e.g., wrap the entire formatted message
construction in an if logger.isEnabledFor(logging.DEBUG) (or if
self.debug_beam_dumps) and change the call to logger.debug; ensure no
tensor-inspecting helpers are invoked outside that guard so seq_slots_long,
beam_search_store.cum_log_probs, beam_search_store.predecessor_beams,
cache_indirection and cache_indirection_buffer are not materialized on hot
paths. Apply the same guarding pattern to the other affected blocks around the
noted regions (roughly the blocks at ~2920-2930, ~3176-3183, ~3381-3393,
~3981-3996, and ~4133-4142) so info-level logs remain metadata-only and heavy
tensor-to-host operations only occur under the debug flag.
In `@tensorrt_llm/serve/openai_disagg_service.py`:
- Around line 509-515: The code assumes ctx_response.choices has at least one
element and does ctx_response.choices[0] unguarded; add a guard that checks if
ctx_response.choices is empty before indexing (e.g., if not
ctx_response.choices: ...), log or return a controlled validation/error response
to the caller instead of allowing IndexError, and only proceed to set choice =
ctx_response.choices[0] when the list is non-empty; update the surrounding
branch that inspects each choice (the loop over ctx_response.choices and the
subsequent choice = ctx_response.choices[0]) to handle the empty-case
consistently.
In `@tests/unittest/_torch/sampler/test_beam_search.py`:
- Around line 382-385: The helper currently builds ctx_disagg_params with
disagg_request_id=1000 + idx which causes duplicate IDs across separate helper
invocations (e.g., in test_beam_search_disagg_e2e()), so update the helper that
constructs ctx_disagg_params to accept a request_id_base (or similar) and
compute disagg_request_id as 1000 + request_id_base + idx; then call that helper
from the two test sites with different request_id_base values (e.g., 0 and 100
or another offset) so validate_disagg_outputs() sees unique IDs; apply the same
change to the other occurrence referenced around lines 526-531.
- Around line 821-854: The test currently bypasses the real disagg seeding by
directly writing into BeamSearchMetadata.cum_log_probs via make_metadata and
thus misses bugs in the production handoff; update the test to call the
production seeding helper instead of mutating cum_log_probs directly (or add an
invocation of the real handoff function that populates generation-side sampler
state) and then call beam_search_sampling_batch; additionally assert that the
handoff-populated buffers (e.g., BeamSearchMetadata.cache_indirection,
predecessor_beams, cum_log_probs and new_log_probs for seq_slots) match the
expected seeded values (continuous_* variables) to ensure the full seeding path
is exercised and validated rather than only post-seed behavior.
- Around line 803-805: The trailing line for the torch.full call causes a
hanging indent (E128); fix it by reformatting the assignment to use standard
4-space indentation and either keep the call on one line or align continuation
under the opening parenthesis. Update the continuous_metadata.seq_lens =
torch.full((batch_size, ), prompt_len + 1, dtype=torch.int32) expression so the
second line is aligned with the first parenthesis or collapsed to a single line,
referencing the torch.full call and variables batch_size and prompt_len.
---
Outside diff comments:
In `@tensorrt_llm/_torch/pyexecutor/py_executor.py`:
- Around line 3575-3590: The code assumes
req.context_phase_params.first_gen_tokens contains exactly beam_width tokens
before looping; add a guard that checks first_gen_tokens is not None and
len(first_gen_tokens) >= beam_width (or == beam_width) before indexing. If the
check fails, log a request-scoped warning via logger including req.py_request_id
and ctx_request_id, mark or fail only this request (do not raise), and skip the
per-beam add_new_token/seed call (or supply safe defaults) so the executor loop
continues; update the block referencing first_gen_tokens, req.add_new_token,
beam_width, and self._seed_disagg_beam_cum_log_probs accordingly.
In `@tensorrt_llm/_torch/pyexecutor/resource_manager.py`:
- Around line 1196-1201: The return type annotation for get_batch_cache_indices
is incorrect for beam-aware calls: when beam_width > 1 the function returns a
nested per-request-per-beam structure (List[List[List[int]]]) but the signature
declares List[List[int]]; update the function's return type to reflect the
beam-aware shape (or add `@overload` signatures to distinguish beam_width==1 vs
>1) and adjust any related type imports so callers and linters correctly see the
List[List[List[int]]] result for multi-beam cases; reference the
get_batch_cache_indices function and its beam_width parameter when making the
change.
🪄 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: 46aa4d7c-85fd-483b-bcc7-6c418ed7fd2c
📒 Files selected for processing (15)
cpp/tensorrt_llm/batch_manager/cacheFormatter.cpptensorrt_llm/_torch/disaggregation/base/transfer.pytensorrt_llm/_torch/disaggregation/native/transfer.pytensorrt_llm/_torch/disaggregation/resource/cache_reuse.pytensorrt_llm/_torch/disaggregation/transceiver.pytensorrt_llm/_torch/pyexecutor/llm_request.pytensorrt_llm/_torch/pyexecutor/py_executor.pytensorrt_llm/_torch/pyexecutor/resource_manager.pytensorrt_llm/_torch/pyexecutor/sampler.pytensorrt_llm/disaggregated_params.pytensorrt_llm/executor/result.pytensorrt_llm/serve/openai_disagg_service.pytensorrt_llm/serve/openai_protocol.pytests/integration/defs/accuracy/test_disaggregated_serving.pytests/unittest/_torch/sampler/test_beam_search.py
💤 Files with no reviewable changes (1)
- cpp/tensorrt_llm/batch_manager/cacheFormatter.cpp
28c15f1 to
7faceb7
Compare
48190bf to
c935474
Compare
|
/bot run |
|
@athena-nv Could you please rebase to resolve the conflicts? |
c935474 to
3b4f20a
Compare
|
/bot run |
Tabrizian
left a comment
There was a problem hiding this comment.
Please register this test to test_beam_search in l0_dgx_b200
3b4f20a to
69d173c
Compare
|
/bot run |
1 similar comment
|
/bot run |
|
PR_Github #52406 [ run ] triggered by Bot. Commit: |
60b52e8 to
1c40a96
Compare
|
/bot run |
1c40a96 to
dea938d
Compare
Signed-off-by: Athena Cai <athenac@nvidia.com>
dea938d to
17071ee
Compare
|
/bot run |
|
/bot run --disable-fail-fast |
|
PR_Github #53187 [ run ] triggered by Bot. Commit: |
|
PR_Github #53187 [ run ] completed with state
|
|
/bot run --disable-fail-fast |
2 similar comments
|
/bot run --disable-fail-fast |
|
/bot run --disable-fail-fast |
|
PR_Github #53349 [ run ] triggered by Bot. Commit: |
|
/bot run --disable-fail-fast |
|
PR_Github #53370 [ run ] triggered by Bot. Commit: |
|
PR_Github #53349 [ run ] completed with state |
|
PR_Github #53370 [ run ] completed with state
|
|
/bot run --disable-fail-fast |
1 similar comment
|
/bot run --disable-fail-fast |
|
PR_Github #53716 [ run ] triggered by Bot. Commit: |
|
PR_Github #53716 [ run ] completed with state |
Tabrizian
left a comment
There was a problem hiding this comment.
LGTM except one comment which can be addressed in a follow up PR.
SimengLiu-nv
left a comment
There was a problem hiding this comment.
The kvcache related changes LGTM.
schetlur-nv
left a comment
There was a problem hiding this comment.
rubber stamp for pending review groups since main review is done
…regation Squash of the post-cherry-pick work layered on top of the 8 DeepSeek-V4 disaggregation cherry-picks. Fixes: - ADP disagg error path: restore per-request hang signal (_event_loop_error), scan all candidates + prefer CTX role for mixed-batch dummy padding, and keep charge_budget=False on KV-transfer timeouts so they don't exhaust the global error budget and shut down the executor. - _count_schedulable_active_requests: revert the GENERATION_TO_COMPLETE exclusion that NVIDIA#13900 added (upstream has no such exclusion). Under V1 ADP it undercounted -> a spurious ADP dummy was inserted on top of a real request -> the batch exceeded max_batch_size=1 and tripped the mamba dummy-mask assert / "No free slots". Restores upstream's exact method (fixes test_ptp_quickstart_advanced_deepseek_v3_lite_4gpus_adp_balance). - _handle_responses KV-timeout: gate the tp_allgather on disagg (kv_cache_transceiver is not None), not just enable_attention_dp. py_kv_transfer_timed_out is disagg-only, so in non-disagg ADP this added a spurious per-iteration collective that desynced adp_router's tp_allgather (gather_all_rank_states received a bool -> TypeError). Verified by 4-GPU DeepSeek-V3-Lite adp_balance e2e A/B (buggy: timeout hang; fixed: completes). - transceiver: only short-circuit the tp_allgather skip when pp_size==1 (_ctx_need_pp_sync) -- the PP>1 path asymmetrically flips send/recv markers across pipeline stages and deadlocks the _ctx_consensus pp_allgather. - py_executor: restore main's immediate benchmark fail-fast guard. - resource_manager: do NOT narrow trim_to_history's except (resize() can raise non-ValueError under v2 SWA + uneven-PP; narrowing leaked KV blocks). Tests (added to existing files): - test_py_executor.py: disagg cache-error sync + ADP no-op paths; ADP dummy-role behavior; GENERATION_TO_COMPLETE counts as active (adp_balance regression); CTX dummy padding for disagg idle ranks (incl. awaiting-KV-transfer-only). - test_kv_cache_v2_scheduler.py: trim_to_history. - test_cache_reuse_adapter.py: trim-to-prompt-history + transceiver ctx mgr; _create_kv_slice TokenRange (stub sets py_beam_width for the NVIDIA#14876 path). - test_router.py: finish_request explicit-session forwarding. - test_agent.py: BindingsNixlTransferStatus + shutdown idempotency (NVIDIA#14137). - transferAgentTest.cpp: status-outlives-agent (weak_ptr UAF safety) + concurrent submitTransferRequests. Signed-off-by: Shixiaowei02 <39303645+Shixiaowei02@users.noreply.github.com>
…regation Squash of the post-cherry-pick work layered on top of the 8 DeepSeek-V4 disaggregation cherry-picks. Fixes: - ADP disagg error path: restore per-request hang signal (_event_loop_error), scan all candidates + prefer CTX role for mixed-batch dummy padding, and keep charge_budget=False on KV-transfer timeouts so they don't exhaust the global error budget and shut down the executor. - _count_schedulable_active_requests: revert the GENERATION_TO_COMPLETE exclusion that NVIDIA#13900 added (upstream has no such exclusion). Under V1 ADP it undercounted -> a spurious ADP dummy was inserted on top of a real request -> the batch exceeded max_batch_size=1 and tripped the mamba dummy-mask assert / "No free slots". Restores upstream's exact method (fixes test_ptp_quickstart_advanced_deepseek_v3_lite_4gpus_adp_balance). - _handle_responses KV-timeout: gate the tp_allgather on disagg (kv_cache_transceiver is not None), not just enable_attention_dp. py_kv_transfer_timed_out is disagg-only, so in non-disagg ADP this added a spurious per-iteration collective that desynced adp_router's tp_allgather (gather_all_rank_states received a bool -> TypeError). Verified by 4-GPU DeepSeek-V3-Lite adp_balance e2e A/B (buggy: timeout hang; fixed: completes). - transceiver: only short-circuit the tp_allgather skip when pp_size==1 (_ctx_need_pp_sync) -- the PP>1 path asymmetrically flips send/recv markers across pipeline stages and deadlocks the _ctx_consensus pp_allgather. - py_executor: restore main's immediate benchmark fail-fast guard. - resource_manager: do NOT narrow trim_to_history's except (resize() can raise non-ValueError under v2 SWA + uneven-PP; narrowing leaked KV blocks). Tests (added to existing files): - test_py_executor.py: disagg cache-error sync + ADP no-op paths; ADP dummy-role behavior; GENERATION_TO_COMPLETE counts as active (adp_balance regression); CTX dummy padding for disagg idle ranks (incl. awaiting-KV-transfer-only). - test_kv_cache_v2_scheduler.py: trim_to_history. - test_cache_reuse_adapter.py: trim-to-prompt-history + transceiver ctx mgr; _create_kv_slice TokenRange (stub sets py_beam_width for the NVIDIA#14876 path). - test_router.py: finish_request explicit-session forwarding. - test_agent.py: BindingsNixlTransferStatus + shutdown idempotency (NVIDIA#14137). - transferAgentTest.cpp: status-outlives-agent (weak_ptr UAF safety) + concurrent submitTransferRequests. Signed-off-by: Shixiaowei02 <39303645+Shixiaowei02@users.noreply.github.com>
…regation Squash of the post-cherry-pick work layered on top of the 8 DeepSeek-V4 disaggregation cherry-picks. Fixes: - ADP disagg error path: restore per-request hang signal (_event_loop_error), scan all candidates + prefer CTX role for mixed-batch dummy padding, and keep charge_budget=False on KV-transfer timeouts so they don't exhaust the global error budget and shut down the executor. - _count_schedulable_active_requests: revert the GENERATION_TO_COMPLETE exclusion that NVIDIA#13900 added (upstream has no such exclusion). Under V1 ADP it undercounted -> a spurious ADP dummy was inserted on top of a real request -> the batch exceeded max_batch_size=1 and tripped the mamba dummy-mask assert / "No free slots". Restores upstream's exact method (fixes test_ptp_quickstart_advanced_deepseek_v3_lite_4gpus_adp_balance). - _handle_responses KV-timeout: gate the tp_allgather on disagg (kv_cache_transceiver is not None), not just enable_attention_dp. py_kv_transfer_timed_out is disagg-only, so in non-disagg ADP this added a spurious per-iteration collective that desynced adp_router's tp_allgather (gather_all_rank_states received a bool -> TypeError). Verified by 4-GPU DeepSeek-V3-Lite adp_balance e2e A/B (buggy: timeout hang; fixed: completes). - transceiver: only short-circuit the tp_allgather skip when pp_size==1 (_ctx_need_pp_sync) -- the PP>1 path asymmetrically flips send/recv markers across pipeline stages and deadlocks the _ctx_consensus pp_allgather. - py_executor: restore main's immediate benchmark fail-fast guard. - resource_manager: do NOT narrow trim_to_history's except (resize() can raise non-ValueError under v2 SWA + uneven-PP; narrowing leaked KV blocks). Tests (added to existing files): - test_py_executor.py: disagg cache-error sync + ADP no-op paths; ADP dummy-role behavior; GENERATION_TO_COMPLETE counts as active (adp_balance regression); CTX dummy padding for disagg idle ranks (incl. awaiting-KV-transfer-only). - test_kv_cache_v2_scheduler.py: trim_to_history. - test_cache_reuse_adapter.py: trim-to-prompt-history + transceiver ctx mgr; _create_kv_slice TokenRange (stub sets py_beam_width for the NVIDIA#14876 path). - test_router.py: finish_request explicit-session forwarding. - test_agent.py: BindingsNixlTransferStatus + shutdown idempotency (NVIDIA#14137). - transferAgentTest.cpp: status-outlives-agent (weak_ptr UAF safety) + concurrent submitTransferRequests. Signed-off-by: Shixiaowei02 <39303645+Shixiaowei02@users.noreply.github.com>
…regation Squash of the post-cherry-pick work layered on top of the 8 DeepSeek-V4 disaggregation cherry-picks. Fixes: - ADP disagg error path: restore per-request hang signal (_event_loop_error), scan all candidates + prefer CTX role for mixed-batch dummy padding, and keep charge_budget=False on KV-transfer timeouts so they don't exhaust the global error budget and shut down the executor. - _count_schedulable_active_requests: revert the GENERATION_TO_COMPLETE exclusion that NVIDIA#13900 added (upstream has no such exclusion). Under V1 ADP it undercounted -> a spurious ADP dummy was inserted on top of a real request -> the batch exceeded max_batch_size=1 and tripped the mamba dummy-mask assert / "No free slots". Restores upstream's exact method (fixes test_ptp_quickstart_advanced_deepseek_v3_lite_4gpus_adp_balance). - _handle_responses KV-timeout: gate the tp_allgather on disagg (kv_cache_transceiver is not None), not just enable_attention_dp. py_kv_transfer_timed_out is disagg-only, so in non-disagg ADP this added a spurious per-iteration collective that desynced adp_router's tp_allgather (gather_all_rank_states received a bool -> TypeError). Verified by 4-GPU DeepSeek-V3-Lite adp_balance e2e A/B (buggy: timeout hang; fixed: completes). - transceiver: only short-circuit the tp_allgather skip when pp_size==1 (_ctx_need_pp_sync) -- the PP>1 path asymmetrically flips send/recv markers across pipeline stages and deadlocks the _ctx_consensus pp_allgather. - py_executor: restore main's immediate benchmark fail-fast guard. - resource_manager: do NOT narrow trim_to_history's except (resize() can raise non-ValueError under v2 SWA + uneven-PP; narrowing leaked KV blocks). Tests (added to existing files): - test_py_executor.py: disagg cache-error sync + ADP no-op paths; ADP dummy-role behavior; GENERATION_TO_COMPLETE counts as active (adp_balance regression); CTX dummy padding for disagg idle ranks (incl. awaiting-KV-transfer-only). - test_kv_cache_v2_scheduler.py: trim_to_history. - test_cache_reuse_adapter.py: trim-to-prompt-history + transceiver ctx mgr; _create_kv_slice TokenRange (stub sets py_beam_width for the NVIDIA#14876 path). - test_router.py: finish_request explicit-session forwarding. - test_agent.py: BindingsNixlTransferStatus + shutdown idempotency (NVIDIA#14137). - transferAgentTest.cpp: status-outlives-agent (weak_ptr UAF safety) + concurrent submitTransferRequests. Signed-off-by: Shixiaowei02 <39303645+Shixiaowei02@users.noreply.github.com>
Description
Added support for beam search in disaggregated serving for the PyTorch backend. Previously, the disaggregated path effectively assumed
beam_width == 1: the Python transfer path did not carry enough beam-aware block metadata, and the generation server did not receive enough first-token beam state to continue scoring from the context server handoff. This change removes those single-beam assumptions while keeping the transceiver block-id contract as a 1-D list.Key changes:
req.py_beam_width;KVCacheManagerpacks per-beam cache indices into the 1-D beam-0-plus-tail layout;_create_kv_slicetrims sliding-window and cached-prefix blocks against the beam-0 prefix while preserving appended beam tails;first_gen_log_probs;first_gen_tokens;original_tokensatprompt_len;cache_indirectionso each first generated token is associated with the correct beam;extra_body={"use_beam_search": True};CompletionOutputobjects instead of only choice 0.CnnDailymailand the existingbeam_width=2reference.claimResult.totalMatchedTokens <= claimResult.numSharedContextBlocks * mTokensPerBlockso that the prepopulated prompt length only includes tokens whose KV blocks were onboarded for reuse.totalMatchedTokensandprepopulatedPromptLengthmust match that block-level reuse boundary.Test Coverage
Added
test_beam_search_sampling_batch_disagg_handoffto compare continuous beam search against a simulated disaggregated handoff. It verifies that generation-side seeding fromfirst_gen_log_probsproduces the same next tokens, cumulative log probabilities, new log probabilities, and predecessor beams as the continuous path, and that leaving the scores unseeded fails to match.Added
test_beam_search_disagg_e2efor context-only followed by generation-only beam search with the Python NIXL transceiver. The test validates generated beam outputs andcache_indirectionshape.Added packed-layout unit tests for block packing,
py_beam_widthpropagation, sender alignment, and_create_kv_slicetrimming/cache-skip behavior.Extended the disaggregated serving accuracy test to run beam search with
max_beam_width=2, Python NIXL KV transfer, and CNN/DailyMailextra_acc_spec="beam_width=2".Added the beam-search disaggregated serving accuracy test to
l0_dgx_b200.yml.Please check this after reviewing the above items as appropriate for this PR.