Skip to content

Commit 83e7903

Browse files
[TRTLLM-12669][feat] Eagle3 one-model draft sampling honors target sampling params
The Eagle3 one-model draft loop was hardcoded to greedy argmax, even when the target sampler used non-greedy (temperature/top_k/top_p) parameters. This made rejection sampling math degenerate: with draft forced to argmax, p_draft is a one-hot on the argmax token, so p_target / p_draft is zero everywhere else and acceptance is biased. Route the linear draft loop through a new `_draft_sampler_advanced` on `SpecWorkerBase` that reuses the per-request sampling tensors already populated by `populate_sampling_params_for_one_model` (request_temperatures / request_top_ks / request_top_ps) and the shared seed/offset used by the target sampler. When the batch is all-greedy (`is_all_greedy_sample=True`) it short-circuits to `_draft_sampler_greedy`, matching the target sampler's argmax fast-path so existing CUDA-graph variants are unaffected. Other spec modes (Draft-Target, MTP, Pard) are untouched and keep their current argmax draft sampling. Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
1 parent d237690 commit 83e7903

2 files changed

Lines changed: 72 additions & 15 deletions

File tree

tensorrt_llm/_torch/speculative/eagle3.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,8 @@ def _forward_linear_draft_loop(self, inputs, attn_metadata, spec_metadata,
717717
if spec_metadata.use_rejection_sampling:
718718
draft_logits_list.append(logits.clone())
719719

720-
new_draft_token = self.draft_decoder(logits, draft_model)
720+
new_draft_token = self.draft_decoder(logits, draft_model,
721+
spec_metadata, batch_size)
721722
next_draft_tokens.append(new_draft_token)
722723
# update inputs
723724
hidden_states = hidden_states_to_save[gather_ids]
@@ -794,27 +795,27 @@ def draft_decoder(
794795
self,
795796
logits: torch.Tensor,
796797
draft_model: nn.Module,
798+
spec_metadata: Optional[Eagle3OneModelSpecMetadata] = None,
799+
batch_size: Optional[int] = None,
797800
):
798801
'''
799-
Sampling draft tokens with support for non-greedy sampling.
802+
Sample draft tokens. When spec_metadata + batch_size are provided, use
803+
the target's per-request sampling params (temperature/top_k/top_p);
804+
otherwise fall back to argmax.
800805
801806
Args:
802-
logits: torch.Tensor
803-
[num_tokens, vocab_size]
804-
Logits produced by the draft model.
805-
draft_model: nn.Module
806-
The draft model.
807-
808-
Returns:
809-
draft_tokens: torch.Tensor
810-
[batch_size * max_draft_len]
811-
Draft token ids. Flattened.
807+
logits: [batch_size, vocab_size] - Draft model logits.
808+
draft_model: The draft model.
809+
spec_metadata: Carries per-request sampling param tensors. When
810+
None, sampling is forced greedy.
811+
batch_size: Active requests, used to slice per-request tensors.
812812
'''
813813

814814
d2t = getattr(draft_model.model, "d2t", None)
815-
draft_tokens = self._draft_sampler_greedy(logits, d2t)
816-
817-
return draft_tokens
815+
if spec_metadata is not None and batch_size is not None:
816+
return self._draft_sampler_advanced(logits, spec_metadata,
817+
batch_size, d2t)
818+
return self._draft_sampler_greedy(logits, d2t)
818819

819820
def prepare_1st_drafter_inputs(
820821
self,

tensorrt_llm/_torch/speculative/interface.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,62 @@ def _draft_sampler_greedy(self, logits: torch.Tensor, d2t=None):
11411141

11421142
return draft_tokens.type(torch.int32)
11431143

1144+
def _draft_sampler_advanced(
1145+
self,
1146+
logits: torch.Tensor,
1147+
spec_metadata: "SpecMetadata",
1148+
batch_size: int,
1149+
d2t: Optional[torch.Tensor] = None,
1150+
):
1151+
"""
1152+
Draft token sampling using per-request sampling parameters from the
1153+
target's sampling config. Falls back to argmax when the batch is
1154+
all-greedy.
1155+
1156+
Args:
1157+
logits: [batch_size, vocab_size] - Draft model logits (one row per
1158+
request, since each draft step emits one token per request).
1159+
spec_metadata: Source of per-request temperatures / top_k / top_p
1160+
tensors populated by populate_sampling_params_for_one_model.
1161+
batch_size: Number of active requests in the batch.
1162+
d2t: Optional dictionary offset tensor for vocab mapping.
1163+
1164+
Returns:
1165+
draft_tokens: [batch_size] - Sampled draft token ids (int32)
1166+
"""
1167+
if spec_metadata.is_all_greedy_sample:
1168+
return self._draft_sampler_greedy(logits, d2t)
1169+
1170+
temperatures = spec_metadata.request_temperatures[:batch_size]
1171+
top_ks = spec_metadata.request_top_ks[:batch_size]
1172+
top_ps = spec_metadata.request_top_ps[:batch_size]
1173+
1174+
if self.use_flashinfer:
1175+
top_ks = top_ks.clamp(min=1, max=logits.shape[-1] - 1)
1176+
if self.seed is None:
1177+
self.seed = torch.tensor([0],
1178+
dtype=torch.int64,
1179+
device=logits.device)
1180+
self.offset = torch.tensor([0],
1181+
dtype=torch.int64,
1182+
device=logits.device)
1183+
self.seed += 1
1184+
self.seed %= (2**31)
1185+
1186+
draft_tokens = sampling_batch_spec_dec_one_model(
1187+
logits,
1188+
temperatures,
1189+
top_ks,
1190+
top_ps,
1191+
use_flashinfer=self.use_flashinfer,
1192+
seed=self.seed,
1193+
offset=self.offset)
1194+
1195+
if d2t is not None:
1196+
draft_tokens = d2t[draft_tokens] + draft_tokens
1197+
1198+
return draft_tokens.type(torch.int32)
1199+
11441200
def _compute_and_store_draft_probs(
11451201
self,
11461202
draft_logits_list: List[torch.Tensor],

0 commit comments

Comments
 (0)