[Spec] Move accept_tokens off EagleDraftInput; pass via method arg (#24735)

This commit is contained in:
Liangsheng Yin
2026-05-08 23:24:18 -07:00
committed by GitHub
parent 1610aa77ab
commit 78da0d3106
7 changed files with 144 additions and 94 deletions
@@ -213,7 +213,6 @@ class DFlashVerifyInput(SpecInput):
last_loc, last_loc,
len(batch.input_ids), len(batch.input_ids),
) )
self.last_loc = last_loc
bs = batch.batch_size() bs = batch.batch_size()
assign_req_to_token_pool_func( assign_req_to_token_pool_func(
+105 -65
View File
@@ -140,7 +140,6 @@ class EagleVerifyInput(SpecInput, EagleVerifyInputV2Mixin):
last_loc, last_loc,
len(batch.input_ids), len(batch.input_ids),
) )
self.last_loc = last_loc
bs = batch.batch_size() bs = batch.batch_size()
assign_req_to_token_pool_func( assign_req_to_token_pool_func(
@@ -241,23 +240,18 @@ class EagleVerifyInput(SpecInput, EagleVerifyInputV2Mixin):
accepted token logits. accepted token logits.
""" """
if batch.forward_mode.is_idle(): if batch.forward_mode.is_idle():
return EagleVerifyOutput( next_draft_input = EagleDraftInput.create_idle_input(
draft_input=EagleDraftInput.create_idle_input( device=batch.device,
device=batch.device, hidden_size=batch.model_config.spec_hidden_size,
hidden_size=batch.model_config.spec_hidden_size, dtype=batch.model_config.dtype,
dtype=batch.model_config.dtype, topk=self.topk,
topk=self.topk, capture_hidden_mode=CaptureHiddenMode.LAST,
capture_hidden_mode=CaptureHiddenMode.LAST, )
), return EagleVerifyOutput.create_idle(
next_draft_input=next_draft_input,
logits_output=logits_output, logits_output=logits_output,
accept_tokens=torch.empty(0, dtype=torch.long, device=batch.device), device=batch.device,
num_accepted_drafts_per_req_cpu=[], spec_steps=self.spec_steps,
accepted_indices=torch.full(
(0, self.spec_steps + 1),
-1,
dtype=torch.int32,
device=batch.device,
),
) )
bs = self.retrieve_index.shape[0] bs = self.retrieve_index.shape[0]
@@ -551,23 +545,22 @@ class EagleVerifyInput(SpecInput, EagleVerifyInputV2Mixin):
batch.seq_lens.add_(num_accepted_drafts + 1) batch.seq_lens.add_(num_accepted_drafts + 1)
batch.seq_lens_cpu.add_(num_accepted_tokens_cpu) batch.seq_lens_cpu.add_(num_accepted_tokens_cpu)
draft_input = EagleDraftInput( next_draft_input = EagleDraftInput(
hidden_states=batch.spec_info.hidden_states[accept_index], hidden_states=batch.spec_info.hidden_states[accept_index],
accept_tokens=accept_tokens,
num_accepted_drafts=num_accepted_drafts, num_accepted_drafts=num_accepted_drafts,
num_accepted_tokens=num_accepted_drafts + 1, num_accepted_tokens=num_accepted_drafts + 1,
num_accepted_drafts_cpu=num_accepted_drafts_list,
num_accepted_tokens_cpu=num_accepted_tokens_list, num_accepted_tokens_cpu=num_accepted_tokens_list,
seq_lens_for_draft_extend=batch.seq_lens,
seq_lens_for_draft_extend_cpu=batch.seq_lens_cpu,
req_pool_indices_for_draft_extend=batch.req_pool_indices,
) )
return EagleVerifyOutput( return EagleVerifyOutput(
draft_input=draft_input, next_draft_input=next_draft_input,
logits_output=logits_output, logits_output=logits_output,
accept_tokens=accept_tokens, accept_tokens=accept_tokens,
num_accepted_drafts_per_req_cpu=draft_input.num_accepted_drafts_cpu, unfinished_accept_tokens=accept_tokens,
seq_lens_for_draft_extend=batch.seq_lens,
seq_lens_for_draft_extend_cpu=batch.seq_lens_cpu,
req_pool_indices_for_draft_extend=batch.req_pool_indices,
num_accepted_drafts_per_req_cpu=num_accepted_drafts_list,
accepted_indices=accept_index, accepted_indices=accept_index,
) )
else: else:
@@ -621,23 +614,36 @@ class EagleVerifyInput(SpecInput, EagleVerifyInputV2Mixin):
unfinished_num_accepted_drafts = num_accepted_drafts[ unfinished_num_accepted_drafts = num_accepted_drafts[
unfinished_index_device unfinished_index_device
] ]
draft_input = EagleDraftInput( unfinished_accept_tokens = predict[unfinished_accept_index]
seq_lens_for_draft_extend = batch.seq_lens[unfinished_index_device]
seq_lens_for_draft_extend_cpu = batch.seq_lens_cpu[unfinished_index]
req_pool_indices_for_draft_extend = batch.req_pool_indices[
unfinished_index_device
]
next_draft_input = EagleDraftInput(
hidden_states=batch.spec_info.hidden_states[ hidden_states=batch.spec_info.hidden_states[
unfinished_accept_index unfinished_accept_index
], ],
accept_tokens=predict[unfinished_accept_index],
num_accepted_drafts_cpu=draft_input_num_accepted_drafts_cpu,
num_accepted_tokens_cpu=draft_input_num_accepted_tokens_cpu, num_accepted_tokens_cpu=draft_input_num_accepted_tokens_cpu,
num_accepted_drafts=unfinished_num_accepted_drafts, num_accepted_drafts=unfinished_num_accepted_drafts,
num_accepted_tokens=unfinished_num_accepted_drafts + 1, num_accepted_tokens=unfinished_num_accepted_drafts + 1,
seq_lens_for_draft_extend=batch.seq_lens[unfinished_index_device],
seq_lens_for_draft_extend_cpu=batch.seq_lens_cpu[unfinished_index],
req_pool_indices_for_draft_extend=batch.req_pool_indices[
unfinished_index_device
],
) )
else: else:
draft_input = EagleDraftInput.create_idle_input( unfinished_accept_tokens = torch.empty(
(0,), dtype=accept_tokens.dtype, device=accept_tokens.device
)
seq_lens_for_draft_extend = torch.empty(
(0,), dtype=batch.seq_lens.dtype, device=batch.seq_lens.device
)
seq_lens_for_draft_extend_cpu = torch.empty(
(0,), dtype=batch.seq_lens_cpu.dtype
)
req_pool_indices_for_draft_extend = torch.empty(
(0,),
dtype=batch.req_pool_indices.dtype,
device=batch.req_pool_indices.device,
)
next_draft_input = EagleDraftInput.create_idle_input(
device=batch.device, device=batch.device,
hidden_size=batch.model_config.spec_hidden_size, hidden_size=batch.model_config.spec_hidden_size,
dtype=batch.model_config.dtype, dtype=batch.model_config.dtype,
@@ -646,9 +652,13 @@ class EagleVerifyInput(SpecInput, EagleVerifyInputV2Mixin):
) )
return EagleVerifyOutput( return EagleVerifyOutput(
draft_input=draft_input, next_draft_input=next_draft_input,
logits_output=logits_output, logits_output=logits_output,
accept_tokens=accept_tokens, accept_tokens=accept_tokens,
unfinished_accept_tokens=unfinished_accept_tokens,
seq_lens_for_draft_extend=seq_lens_for_draft_extend,
seq_lens_for_draft_extend_cpu=seq_lens_for_draft_extend_cpu,
req_pool_indices_for_draft_extend=req_pool_indices_for_draft_extend,
num_accepted_drafts_per_req_cpu=num_accepted_drafts_list, num_accepted_drafts_per_req_cpu=num_accepted_drafts_list,
accepted_indices=accept_index, accepted_indices=accept_index,
) )
@@ -660,7 +670,10 @@ class EagleDraftInput(SpecInput, EagleDraftInputV2Mixin):
# shape: (b, topk) # shape: (b, topk)
topk_p: torch.Tensor = None topk_p: torch.Tensor = None
topk_index: torch.Tensor = None topk_index: torch.Tensor = None
# shape: (b, hidden_size) # shape: (b, hidden_size) when consumed by `draft` forward (one hidden per req);
# shape: (total_accepted, hidden_size) when consumed by `draft_extend` forward
# (one hidden per accepted token). Workers maintain this invariant locally;
# there is no type-level guard. Don't add new readers without checking phase.
hidden_states: torch.Tensor = None hidden_states: torch.Tensor = None
capture_hidden_mode: CaptureHiddenMode = CaptureHiddenMode.FULL capture_hidden_mode: CaptureHiddenMode = CaptureHiddenMode.FULL
@@ -670,16 +683,10 @@ class EagleDraftInput(SpecInput, EagleDraftInputV2Mixin):
# `num_accepted_tokens = num_accepted_drafts + 1` (per-req, one bonus per req). # `num_accepted_tokens = num_accepted_drafts + 1` (per-req, one bonus per req).
# Storing both avoids repeated `+ 1` at every consumer (attn backends, kernels). # Storing both avoids repeated `+ 1` at every consumer (attn backends, kernels).
bonus_tokens: torch.Tensor = None bonus_tokens: torch.Tensor = None
# Flat accepted-token tensor for draft-extend, shape `[sum_accepted]`.
# Set right after verify and consumed by `prepare_extend_after_decode` as
# the extend batch's `input_ids`. Dead after that method returns.
# TODO: drop this field and pass `accept_tokens` directly to
# `prepare_extend_after_decode` as a method arg. Its lifetime is bounded
# by verify -> prepare_extend, no need to live on the dataclass.
accept_tokens: torch.Tensor = None
num_accepted_drafts: torch.Tensor = None num_accepted_drafts: torch.Tensor = None
num_accepted_tokens: torch.Tensor = None num_accepted_tokens: torch.Tensor = None
num_accepted_drafts_cpu: List[int] = None # Read by attention backends during draft-extend forward; kept on the
# dataclass because the backends access it via `forward_batch.spec_info`.
num_accepted_tokens_cpu: List[int] = None num_accepted_tokens_cpu: List[int] = None
# Inputs for the attention backends # Inputs for the attention backends
@@ -691,12 +698,6 @@ class EagleDraftInput(SpecInput, EagleDraftInputV2Mixin):
num_tokens_per_req: int = -1 num_tokens_per_req: int = -1
num_tokens_for_logprob_per_req: int = -1 num_tokens_for_logprob_per_req: int = -1
# Inputs for draft extend
# shape: (b,)
seq_lens_for_draft_extend: torch.Tensor = None
seq_lens_for_draft_extend_cpu: torch.Tensor = None
req_pool_indices_for_draft_extend: torch.Tensor = None
# Inputs for V2 overlap worker # Inputs for V2 overlap worker
future_indices: Optional[FutureIndices] = None future_indices: Optional[FutureIndices] = None
new_seq_lens: Optional[torch.Tensor] = None new_seq_lens: Optional[torch.Tensor] = None
@@ -735,7 +736,6 @@ class EagleDraftInput(SpecInput, EagleDraftInputV2Mixin):
): ):
return cls( return cls(
bonus_tokens=torch.empty((0,), device=device, dtype=torch.int32), bonus_tokens=torch.empty((0,), device=device, dtype=torch.int32),
accept_tokens=torch.empty((0,), device=device, dtype=torch.int32),
hidden_states=torch.empty((0, hidden_size), device=device, dtype=dtype), hidden_states=torch.empty((0, hidden_size), device=device, dtype=dtype),
topk_p=torch.empty((0, topk), device=device, dtype=torch.float32), topk_p=torch.empty((0, topk), device=device, dtype=torch.float32),
topk_index=torch.empty((0, topk), device=device, dtype=torch.int64), topk_index=torch.empty((0, topk), device=device, dtype=torch.int64),
@@ -743,29 +743,29 @@ class EagleDraftInput(SpecInput, EagleDraftInputV2Mixin):
new_seq_lens=torch.empty((0,), device=device, dtype=torch.int32), new_seq_lens=torch.empty((0,), device=device, dtype=torch.int32),
num_accepted_drafts=torch.empty((0,), device=device, dtype=torch.int32), num_accepted_drafts=torch.empty((0,), device=device, dtype=torch.int32),
num_accepted_tokens=torch.empty((0,), device=device, dtype=torch.int32), num_accepted_tokens=torch.empty((0,), device=device, dtype=torch.int32),
num_accepted_drafts_cpu=[],
num_accepted_tokens_cpu=[], num_accepted_tokens_cpu=[],
) )
def prepare_extend_after_decode( def prepare_extend_after_decode(
self, self,
batch: ScheduleBatch, batch: ScheduleBatch,
verify_output: "EagleVerifyOutput",
speculative_num_steps: int, speculative_num_steps: int,
): ):
if batch.forward_mode.is_idle(): if batch.forward_mode.is_idle():
return return
# `self.accept_tokens` is the flat accepted-token tensor set by # All transient verify->extend handoff state is read off `verify_output`,
# `EagleVerifyInput.verify`; use it as the extend batch's `input_ids`. # not from `self`. The kernel below populates `self.bonus_tokens`
# The kernel below populates `self.bonus_tokens` ([bs] per-req) for # ([bs] per-req) for the next decode round; that is the only state on
# the next decode round. # `self` that survives past this method.
batch.input_ids = self.accept_tokens batch.input_ids = verify_output.unfinished_accept_tokens
batch.extend_lens = batch.spec_info.num_accepted_tokens_cpu batch.extend_lens = batch.spec_info.num_accepted_tokens_cpu
batch.extend_num_tokens = sum(batch.extend_lens) batch.extend_num_tokens = sum(batch.extend_lens)
batch.seq_lens = batch.spec_info.seq_lens_for_draft_extend batch.seq_lens = verify_output.seq_lens_for_draft_extend
batch.seq_lens_cpu = batch.spec_info.seq_lens_for_draft_extend_cpu batch.seq_lens_cpu = verify_output.seq_lens_for_draft_extend_cpu
batch.req_pool_indices = batch.spec_info.req_pool_indices_for_draft_extend batch.req_pool_indices = verify_output.req_pool_indices_for_draft_extend
batch.return_logprob = False batch.return_logprob = False
batch.return_hidden_states = False batch.return_hidden_states = False
@@ -873,13 +873,53 @@ class EagleDraftInput(SpecInput, EagleDraftInputV2Mixin):
@dataclass @dataclass
class EagleVerifyOutput: class EagleVerifyOutput:
# Draft input batch # Next iter's persistent draft state, ready to be installed as `batch.spec_info`.
draft_input: EagleDraftInput next_draft_input: EagleDraftInput
# Logit outputs from target worker # Logit outputs from target worker.
logits_output: LogitsProcessorOutput logits_output: LogitsProcessorOutput
# Accepted token ids including the bonus token (flat, [sum_accepted]) # All accepted tokens flat across all reqs incl. those that finished this
# step. Includes the bonus token. Used for output processing.
accept_tokens: torch.Tensor accept_tokens: torch.Tensor
# Accepted token length per sequence in a batch in CPU. # Below are transient handoff fields for the next iter's draft-extend pass.
# They are scoped to the verify -> prepare_extend_after_decode window only;
# `prepare_extend_after_decode` reads them off this object via method arg
# rather than smuggling them through `EagleDraftInput`.
#
# Subset of `accept_tokens` for reqs continuing into next iter's draft-extend
# forward (= `accept_tokens` when no req finished; flat over unfinished
# reqs only otherwise). Becomes `batch.input_ids` for that forward pass.
unfinished_accept_tokens: torch.Tensor
# `batch.seq_lens` / `batch.seq_lens_cpu` / `batch.req_pool_indices` to
# use for the next iter's draft-extend forward; sliced to surviving reqs.
seq_lens_for_draft_extend: torch.Tensor
seq_lens_for_draft_extend_cpu: torch.Tensor
req_pool_indices_for_draft_extend: torch.Tensor
# Accepted token length per sequence in a batch in CPU (full set).
num_accepted_drafts_per_req_cpu: List[int] num_accepted_drafts_per_req_cpu: List[int]
# Accepted indices from logits_output.next_token_logits # Accepted indices from logits_output.next_token_logits
accepted_indices: torch.Tensor accepted_indices: torch.Tensor
@classmethod
def create_idle(
cls,
*,
next_draft_input: EagleDraftInput,
logits_output: LogitsProcessorOutput,
device: torch.device,
spec_steps: int,
) -> "EagleVerifyOutput":
return cls(
next_draft_input=next_draft_input,
logits_output=logits_output,
accept_tokens=torch.empty(0, dtype=torch.long, device=device),
unfinished_accept_tokens=torch.empty(0, dtype=torch.long, device=device),
seq_lens_for_draft_extend=torch.empty(0, dtype=torch.int32, device=device),
seq_lens_for_draft_extend_cpu=torch.empty(0, dtype=torch.int32),
req_pool_indices_for_draft_extend=torch.empty(
0, dtype=torch.int64, device=device
),
num_accepted_drafts_per_req_cpu=[],
accepted_indices=torch.full(
(0, spec_steps + 1), -1, dtype=torch.int32, device=device
),
)
+13 -8
View File
@@ -505,10 +505,10 @@ class EAGLEWorker(TpModelWorker):
# when DP attention is enabled, but it is slow. Skip it for now. # when DP attention is enabled, but it is slow. Skip it for now.
if ( if (
self.server_args.enable_dp_attention self.server_args.enable_dp_attention
or batch.spec_info.accept_tokens.shape[0] > 0 or verify_output.unfinished_accept_tokens.shape[0] > 0
): ):
# decode is not finished # decode is not finished
self.forward_draft_extend_after_decode(batch) self.forward_draft_extend_after_decode(batch, verify_output)
set_time_batch( set_time_batch(
batch.reqs, "set_spec_draft_extend_end_time", trace_only=True batch.reqs, "set_spec_draft_extend_end_time", trace_only=True
@@ -527,8 +527,10 @@ class EAGLEWorker(TpModelWorker):
can_run_cuda_graph=can_run_cuda_graph, can_run_cuda_graph=can_run_cuda_graph,
) )
def check_forward_draft_extend_after_decode(self, batch: ScheduleBatch): def check_forward_draft_extend_after_decode(
local_need_forward = batch.spec_info.accept_tokens.shape[0] > 0 self, batch: ScheduleBatch, verify_output: EagleVerifyOutput
):
local_need_forward = verify_output.unfinished_accept_tokens.shape[0] > 0
if not self.server_args.enable_dp_attention: if not self.server_args.enable_dp_attention:
return local_need_forward return local_need_forward
@@ -977,7 +979,7 @@ class EAGLEWorker(TpModelWorker):
batch.forward_mode = ( batch.forward_mode = (
ForwardMode.DECODE if not batch.forward_mode.is_idle() else ForwardMode.IDLE ForwardMode.DECODE if not batch.forward_mode.is_idle() else ForwardMode.IDLE
) )
batch.spec_info = res.draft_input batch.spec_info = res.next_draft_input
return logits_output, res, model_worker_batch, can_run_cuda_graph return logits_output, res, model_worker_batch, can_run_cuda_graph
@@ -1104,7 +1106,9 @@ class EAGLEWorker(TpModelWorker):
assert forward_batch.spec_info is batch.spec_info assert forward_batch.spec_info is batch.spec_info
self.capture_for_decode(logits_output, forward_batch.spec_info) self.capture_for_decode(logits_output, forward_batch.spec_info)
def forward_draft_extend_after_decode(self, batch: ScheduleBatch): def forward_draft_extend_after_decode(
self, batch: ScheduleBatch, verify_output: EagleVerifyOutput
):
assert isinstance(batch.spec_info, EagleDraftInput) assert isinstance(batch.spec_info, EagleDraftInput)
# Backup fields that will be modified in-place # Backup fields that will be modified in-place
seq_lens_backup = batch.seq_lens.clone() seq_lens_backup = batch.seq_lens.clone()
@@ -1116,7 +1120,7 @@ class EAGLEWorker(TpModelWorker):
input_is_idle = batch.forward_mode.is_idle() input_is_idle = batch.forward_mode.is_idle()
if not input_is_idle and batch.spec_info.accept_tokens.numel() == 0: if not input_is_idle and verify_output.unfinished_accept_tokens.numel() == 0:
batch = batch.copy() batch = batch.copy()
batch.prepare_for_idle() batch.prepare_for_idle()
hidden_size = ( hidden_size = (
@@ -1137,7 +1141,8 @@ class EAGLEWorker(TpModelWorker):
batch.spec_info.num_tokens_for_logprob_per_req = 1 batch.spec_info.num_tokens_for_logprob_per_req = 1
batch.spec_info.prepare_extend_after_decode( batch.spec_info.prepare_extend_after_decode(
batch, batch,
self.speculative_num_steps, verify_output=verify_output,
speculative_num_steps=self.speculative_num_steps,
) )
batch.forward_mode = ( batch.forward_mode = (
ForwardMode.DRAFT_EXTEND ForwardMode.DRAFT_EXTEND
@@ -62,7 +62,7 @@ class FrozenKVMTPVerifyInput(EagleVerifyInput):
def verify(self, *args, **kwargs) -> EagleVerifyOutput: def verify(self, *args, **kwargs) -> EagleVerifyOutput:
output = super().verify(*args, **kwargs) output = super().verify(*args, **kwargs)
output.draft_input = _to_frozen_kv_mtp_draft_input(output.draft_input) output.next_draft_input = _to_frozen_kv_mtp_draft_input(output.next_draft_input)
return output return output
@@ -43,6 +43,7 @@ from sglang.srt.model_executor.pool_configurator import MemoryPoolConfig
from sglang.srt.observability.req_time_stats import set_time_batch from sglang.srt.observability.req_time_stats import set_time_batch
from sglang.srt.observability.trace import get_global_tracing_enabled from sglang.srt.observability.trace import get_global_tracing_enabled
from sglang.srt.server_args import ServerArgs from sglang.srt.server_args import ServerArgs
from sglang.srt.speculative.eagle_info import EagleVerifyOutput
from sglang.srt.speculative.eagle_utils import ( from sglang.srt.speculative.eagle_utils import (
build_tree_kernel_efficient, build_tree_kernel_efficient,
organize_draft_results, organize_draft_results,
@@ -463,7 +464,7 @@ class FrozenKVMTPWorker(TpModelWorker):
self.server_args.enable_dp_attention self.server_args.enable_dp_attention
or batch.spec_info.bonus_tokens.numel() or batch.spec_info.bonus_tokens.numel()
): ):
self.forward_draft_extend_after_decode(batch) self.forward_draft_extend_after_decode(batch, verify_output)
set_time_batch(batch.reqs, "set_spec_draft_extend_end_time", trace_only=True) set_time_batch(batch.reqs, "set_spec_draft_extend_end_time", trace_only=True)
return GenerationBatchResult( return GenerationBatchResult(
@@ -504,7 +505,9 @@ class FrozenKVMTPWorker(TpModelWorker):
mm_input_embeds=mm_input_embeds, mm_input_embeds=mm_input_embeds,
) )
def forward_draft_extend_after_decode(self, batch: ScheduleBatch) -> None: def forward_draft_extend_after_decode(
self, batch: ScheduleBatch, verify_output: EagleVerifyOutput
) -> None:
assert isinstance(batch.spec_info, FrozenKVMTPDraftInput) assert isinstance(batch.spec_info, FrozenKVMTPDraftInput)
input_is_idle = batch.forward_mode.is_idle() input_is_idle = batch.forward_mode.is_idle()
if not input_is_idle and batch.spec_info.bonus_tokens.numel() == 0: if not input_is_idle and batch.spec_info.bonus_tokens.numel() == 0:
@@ -527,19 +530,18 @@ class FrozenKVMTPWorker(TpModelWorker):
req_pool_indices_backup = batch.req_pool_indices req_pool_indices_backup = batch.req_pool_indices
try: try:
if draft_input.seq_lens_for_draft_extend is not None: # Verify may leave finished requests in ScheduleBatch; seed only
# Verify may leave finished requests in ScheduleBatch; seed only # the unfinished requests carried by `verify_output`.
# the unfinished requests carried by draft_input. batch.seq_lens = verify_output.seq_lens_for_draft_extend
batch.seq_lens = draft_input.seq_lens_for_draft_extend batch.seq_lens_cpu = verify_output.seq_lens_for_draft_extend_cpu
batch.seq_lens_cpu = draft_input.seq_lens_for_draft_extend_cpu batch.req_pool_indices = verify_output.req_pool_indices_for_draft_extend
batch.req_pool_indices = draft_input.req_pool_indices_for_draft_extend
last_token_ids, last_hidden = self._select_last_verified_seed(draft_input) last_token_ids, last_hidden = self._select_last_verified_seed(draft_input)
self._run_assistant_seed_step( self._run_assistant_seed_step(
batch, batch,
last_token_ids, last_token_ids,
last_hidden, last_hidden,
seq_lens_cpu=draft_input.seq_lens_for_draft_extend_cpu, seq_lens_cpu=verify_output.seq_lens_for_draft_extend_cpu,
draft_input=draft_input, draft_input=draft_input,
) )
finally: finally:
@@ -766,7 +768,7 @@ class FrozenKVMTPWorker(TpModelWorker):
batch.forward_mode = ( batch.forward_mode = (
ForwardMode.DECODE if not batch.forward_mode.is_idle() else ForwardMode.IDLE ForwardMode.DECODE if not batch.forward_mode.is_idle() else ForwardMode.IDLE
) )
batch.spec_info = res.draft_input batch.spec_info = res.next_draft_input
del seq_lens_pre_verify del seq_lens_pre_verify
return logits_output, res, model_worker_batch, can_run_cuda_graph return logits_output, res, model_worker_batch, can_run_cuda_graph
@@ -283,10 +283,10 @@ class MultiLayerEagleWorker(TpModelWorker):
# when DP attention is enabled, but it is slow. Skip it for now. # when DP attention is enabled, but it is slow. Skip it for now.
if ( if (
self.server_args.enable_dp_attention self.server_args.enable_dp_attention
or batch.spec_info.accept_tokens.shape[0] > 0 or verify_output.unfinished_accept_tokens.shape[0] > 0
): ):
# decode is not finished # decode is not finished
self.forward_draft_extend_after_decode(batch) self.forward_draft_extend_after_decode(batch, verify_output)
return GenerationBatchResult( return GenerationBatchResult(
logits_output=logits_output, logits_output=logits_output,
@@ -295,8 +295,10 @@ class MultiLayerEagleWorker(TpModelWorker):
can_run_cuda_graph=can_run_cuda_graph, can_run_cuda_graph=can_run_cuda_graph,
) )
def check_forward_draft_extend_after_decode(self, batch: ScheduleBatch): def check_forward_draft_extend_after_decode(
local_need_forward = batch.spec_info.accept_tokens.shape[0] > 0 self, batch: ScheduleBatch, verify_output: EagleVerifyOutput
):
local_need_forward = verify_output.unfinished_accept_tokens.shape[0] > 0
if not self.server_args.enable_dp_attention: if not self.server_args.enable_dp_attention:
return local_need_forward return local_need_forward
@@ -589,7 +591,7 @@ class MultiLayerEagleWorker(TpModelWorker):
batch.forward_mode = ( batch.forward_mode = (
ForwardMode.DECODE if not batch.forward_mode.is_idle() else ForwardMode.IDLE ForwardMode.DECODE if not batch.forward_mode.is_idle() else ForwardMode.IDLE
) )
batch.spec_info = res.draft_input batch.spec_info = res.next_draft_input
return logits_output, res, model_worker_batch, can_run_cuda_graph return logits_output, res, model_worker_batch, can_run_cuda_graph
@@ -652,7 +654,9 @@ class MultiLayerEagleWorker(TpModelWorker):
forward_batch.spec_info.topk_p = torch.cat(topk_p_list, dim=1) forward_batch.spec_info.topk_p = torch.cat(topk_p_list, dim=1)
forward_batch.spec_info.topk_index = torch.cat(topk_index_list, dim=1) forward_batch.spec_info.topk_index = torch.cat(topk_index_list, dim=1)
def forward_draft_extend_after_decode(self, batch: ScheduleBatch): def forward_draft_extend_after_decode(
self, batch: ScheduleBatch, verify_output: EagleVerifyOutput
):
assert isinstance(batch.spec_info, EagleDraftInput) assert isinstance(batch.spec_info, EagleDraftInput)
# Backup fields that will be modified in-place # Backup fields that will be modified in-place
seq_lens_backup = batch.seq_lens.clone() seq_lens_backup = batch.seq_lens.clone()
@@ -664,7 +668,7 @@ class MultiLayerEagleWorker(TpModelWorker):
input_is_idle = batch.forward_mode.is_idle() input_is_idle = batch.forward_mode.is_idle()
if not input_is_idle and batch.spec_info.accept_tokens.numel() == 0: if not input_is_idle and verify_output.unfinished_accept_tokens.numel() == 0:
batch = batch.copy() batch = batch.copy()
batch.prepare_for_idle() batch.prepare_for_idle()
hidden_size = ( hidden_size = (
@@ -684,7 +688,8 @@ class MultiLayerEagleWorker(TpModelWorker):
batch.spec_info.num_tokens_for_logprob_per_req = 1 batch.spec_info.num_tokens_for_logprob_per_req = 1
batch.spec_info.prepare_extend_after_decode( batch.spec_info.prepare_extend_after_decode(
batch, batch,
self.speculative_num_steps, verify_output=verify_output,
speculative_num_steps=self.speculative_num_steps,
) )
batch.forward_mode = ( batch.forward_mode = (
ForwardMode.DRAFT_EXTEND ForwardMode.DRAFT_EXTEND
@@ -106,7 +106,6 @@ class NgramVerifyInput(SpecInput):
last_loc, last_loc,
len(batch.input_ids), len(batch.input_ids),
) )
self.last_loc = last_loc
bs = batch.batch_size() bs = batch.batch_size()
assign_req_to_token_pool[(bs,)]( assign_req_to_token_pool[(bs,)](