[Spec] Unify decode KV-commit bookkeeping across spec-v2 workers (#28754)

This commit is contained in:
Liangsheng Yin
2026-06-23 00:53:05 -07:00
committed by GitHub
parent 743ce88bc5
commit 854c688121
5 changed files with 21 additions and 30 deletions
@@ -558,13 +558,10 @@ class SchedulerBatchResultProcessor:
for i, req in enumerate(batch.reqs):
accept_tokens = next_token_ids[i * stride : i * stride + accept_lens[i]]
if req.is_retracted:
# reset_for_retract() already zeroes committed/allocated KV.
if req.is_retracted or req.finished():
# Nothing to settle: no worker pre-claims the bonus, so
# kv_committed_len already holds the committed prefix.
pass
elif req.finished():
if not batch.spec_algorithm.is_dflash():
# EAGLE prepare_for_decode pre-claimed the bonus slot.
req.kv_committed_len -= 1
else:
if req.grammar is not None:
# Stop accepting once the grammar terminates, so the
@@ -573,13 +570,9 @@ class SchedulerBatchResultProcessor:
# grammar.finished.
accept_tokens = self._accept_grammar_tokens(req, accept_tokens)
# Commit the full accepted run (drafts + bonus).
num_accept_tokens = len(accept_tokens)
if batch.spec_algorithm.is_dflash():
# DFLASH materialized accepted draft tokens plus the bonus token.
req.kv_committed_len += num_accept_tokens
else:
# EAGLE prepare_for_decode pre-claimed the bonus slot.
req.kv_committed_len += num_accept_tokens - 1
req.kv_committed_len += num_accept_tokens
req.spec_verify_ct += 1
num_correct_drafts = result.num_correct_drafts_per_req_cpu[i]
@@ -331,9 +331,15 @@ class StreamingSession(BasePrefixCache):
finished_len = (
req.finished_len if req.finished_len is not None else len(req.output_ids)
)
target = len(req.origin_input_ids) + finished_len
self._trim_overshoot(req, finished_len)
slot.save_from_req(req, is_first=is_first)
# Inherit the authoritative finished length on the slot, not the lagging
# req clock (under overlap + honest committed the clock lags the in-flight
# verify by ~1, which would short-change inheritance). Clamp to allocated
# to keep committed <= allocated for prepare_for_decode.
slot.kv_committed_len = min(target, slot.kv_allocated_len)
# Update req_nodes to this successfully finished request.
req.session.finish_req(req)
@@ -47,19 +47,15 @@ class EagleDraftInputV2Mixin:
num_needed_tokens = 0
for i, r in enumerate(batch.reqs):
cur = r.kv_allocated_len
# max(cur, ...) clamps so adaptive downswitch (smaller alloc_len_per_decode)
# cannot make nxt < cur and corrupt allocator state. kv_committed_len lags
# batch.seq_lens by ~1 verify in overlap mode, so we react to adaptive
# switches one batch later than a seq_lens-based baseline; the 2*alloc
# over-allocation buffer absorbs that lag.
# max(cur, ...) clamps so adaptive downswitch cannot make nxt < cur.
# kv_committed_len is honest (bonus committed in resolve, not here),
# so it lags batch.seq_lens by ~1 verify in overlap; 2*alloc absorbs.
nxt = max(cur, r.kv_committed_len + double_alloc)
cur_kv_lens[i] = cur
nxt_kv_lens[i] = nxt
num_needed_tokens += nxt - cur
r.kv_allocated_len = nxt
r.decode_batch_idx += 1
# Pre-claim bonus slot here (like normal decode); resolve subtracts 1.
r.kv_committed_len += 1
cur_kv_lens_cpu = torch.tensor(cur_kv_lens, dtype=torch.int32, device="cpu")
nxt_kv_lens_cpu = torch.tensor(nxt_kv_lens, dtype=torch.int32, device="cpu")