Fix the EAGLE chunked-prefill next-token chain (#26329) (#26800)

This commit is contained in:
fzyzcjy
2026-05-31 09:48:30 +08:00
committed by GitHub
parent 45194794d0
commit bad83ab427
4 changed files with 37 additions and 1 deletions
@@ -1463,6 +1463,17 @@ def set_mamba_track_indices_from_reqs(batch):
)
def _compute_chunked_req_next_prompt_token(
chunked_req: Optional[Req],
) -> Optional[int]:
if chunked_req is None:
return None
fill_len = len(chunked_req.fill_ids)
if fill_len >= len(chunked_req.origin_input_ids):
return None
return int(chunked_req.origin_input_ids[fill_len])
@dataclasses.dataclass
class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
"""Store all information of a batch on the scheduler."""
@@ -1494,6 +1505,7 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
# For chunked prefill in PP
chunked_req: Optional[Req] = None
chunked_req_next_prompt_token: Optional[int] = None
contains_last_prefill_chunk: bool = True
# For DP attention
@@ -1660,6 +1672,9 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
return_hidden_states=any(req.return_hidden_states for req in reqs),
is_prefill_only=all(req.is_prefill_only for req in reqs),
chunked_req=chunked_req,
chunked_req_next_prompt_token=_compute_chunked_req_next_prompt_token(
chunked_req
),
dllm_config=dllm_config,
)
return batch
@@ -66,10 +66,27 @@ def apply_eagle_prefill_input_rotation(
seg_ends = extend_lens.cumsum(0) - 1
rotated = torch.empty_like(batch.input_ids)
rotated[:-1] = batch.input_ids[1:]
# TODO: chunked-prefill chain divergence at non-final-chunk seg end; fix per PR #26329.
rotated[seg_ends] = next_token_ids.to(batch.input_ids.dtype)
batch.input_ids = rotated
def _eagle_prefill_tail_tokens(
batch: ScheduleBatch, next_token_ids: torch.Tensor
) -> torch.Tensor:
"""Per-seq tail token for EAGLE prefill rotation; uses next prompt token for
non-final chunks (chunked-prefill chain consistency, see PR #26329)."""
tail_tokens = next_token_ids.to(batch.input_ids.dtype)
next_prompt_token = batch.chunked_req_next_prompt_token
if next_prompt_token is not None:
for i, r in enumerate(batch.reqs):
if r is batch.chunked_req:
tail_tokens = tail_tokens.clone()
tail_tokens[i] = next_prompt_token
break
return tail_tokens
def organize_draft_results(
score_list: List[torch.Tensor],
token_list: List[torch.Tensor],
@@ -55,6 +55,7 @@ from sglang.srt.speculative.eagle_info_v2 import (
)
from sglang.srt.speculative.eagle_utils import (
TreeMaskMode,
_eagle_prefill_tail_tokens,
build_tree_kernel_efficient,
per_step_draft_out_cache_loc,
)
@@ -579,11 +580,13 @@ class EagleDraftWorker(BaseDraftWorker):
"""
# Construct input_ids
if not batch.forward_mode.is_idle():
# Chunked-prefill-aware tail tokens (see PR #26329).
tail_tokens = _eagle_prefill_tail_tokens(batch, next_token_ids)
pt = 0
for i, extend_len in enumerate(batch.extend_lens):
input_ids = batch.input_ids[pt : pt + extend_len]
batch.input_ids[pt : pt + extend_len] = torch.cat(
(input_ids[1:], next_token_ids[i].reshape(1))
(input_ids[1:], tail_tokens[i].reshape(1))
)
pt += extend_len
@@ -410,6 +410,7 @@ class MultiLayerEagleDraftWorker(BaseDraftWorker):
forward_batch = ForwardBatch.init_new(batch, self.draft_runner_list[0])
# Construct input_ids
# TODO: same chunked-prefill chain divergence as PR #26329.
if not batch.forward_mode.is_idle():
rotate_input_ids_triton(
forward_batch.input_ids,