Skip to content

Commit ff1089d

Browse files
committed
fix: fix issuse raised by copilot
1 parent 932838f commit ff1089d

5 files changed

Lines changed: 15 additions & 10 deletions

File tree

docs/en/advance/spec_decoding.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,6 @@ The grammar mask is applied at two stages:
121121
1. **Draft model** — forked grammar matchers are used to mask each draft position serially. Each position's mask depends on the token accepted at the previous position, ensuring the draft model proposes grammatically valid tokens.
122122
2. **Target model verification** — position-serial grammar masking is applied to the target model's logits. After rejection sampling, only the accepted tokens are fed back to the original (un-forked) grammar matchers, keeping them in sync for the next step.
123123

124-
For **Eagle3**, the draft vocabulary may differ from the target vocabulary, so a grammar mask cannot be directly applied to draft-vocab logits. In this case, the draft model proposes freely and grammar constraints are enforced solely on the target side via rejection sampling.
125-
126124
### pipeline
127125

128126
```python

docs/zh_cn/advance/spec_decoding.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,6 @@ deepseek-ai/DeepSeek-V3 \
120120
1. **草稿模型** — 使用 fork 出的语法匹配器,逐位置串行施加掩码。每个位置的掩码依赖于前一位置接受的 token,确保草稿模型提出符合语法的 token。
121121
2. **主模型验证** — 对主模型的 logits 进行逐位置串行的语法掩码处理。拒绝采样后,仅将接受的 token 反馈给原始(未 fork 的)语法匹配器,使其为下一步保持正确的状态。
122122

123-
对于 **Eagle3**,草稿词表可能与目标词表不同,因此无法直接对草稿词表的 logits 施加目标语法掩码。此时草稿模型自由提出 token,语法约束完全由主模型侧的拒绝采样来保证。
124-
125123
### pipeline
126124

127125
```python

lmdeploy/pytorch/spec_decode/proposers/eagle3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def _translate_bitmask(self, target_bitmask: torch.Tensor) -> torch.Tensor:
5858
bits_to_set = draft_valid << draft_bits
5959
n_draft_words = (draft_valid.size(1) + 31) // 32
6060
out = target_bitmask.new_zeros(target_bitmask.size(0), n_draft_words)
61-
out.scatter_add_(1, draft_words.unsqueeze(0).expand(target_bitmask.size(0), -1),
61+
out.scatter_add_(1, draft_words.unsqueeze(0).expand(target_bitmask.size(0).to(torch.int64), -1),
6262
bits_to_set)
6363
return out
6464

lmdeploy/pytorch/spec_decode/spec_agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def _expand_sampling_inputs(sampling_inputs: SamplingInputs, num_tokens: int) ->
6363
pass
6464
elif k == 'batch_size':
6565
v = sampling_inputs.batch_size * num_tokens
66-
elif isinstance(v, (list, tuple)) and v is not None:
66+
elif isinstance(v, (list, tuple)) and len(v) == sampling_inputs.batch_size:
6767
v = type(v)(_item for elem in v for _item in [elem] * num_tokens)
6868
out_dict[k] = v
6969

@@ -506,8 +506,8 @@ async def _guided_spec_logits_process(
506506
# subsequent positions get the correct mask.
507507
forked = {idx: proc.fork() for idx, proc in guided_processors.items()}
508508

509+
guided_bitmask = guided_manager.allocate_batched_bitmap(batch_size)
509510
for pos in range(num_expand):
510-
guided_bitmask = guided_manager.allocate_batched_bitmap(batch_size)
511511

512512
for idx, fork_proc in forked.items():
513513
guided_manager.fill_bitmap(fork_proc, guided_bitmask, idx)

tests/pytorch/spec_decode/test_guided_spec_integration.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,13 +1104,22 @@ def test_get_outputs_accept_token_advances_fork(self, compiler, tokenizer_info,
11041104
draft_token_ids, _, _ = proposer.get_outputs(
11051105
model_outputs, model_inputs, guided_processors={0: draft_fork})
11061106

1107-
# Original matcher should be unchanged
1107+
# Original matcher should be unchanged, while the fork should reflect
1108+
# acceptance of the emitted token. Since immediate allowed-id sets may
1109+
# legitimately coincide for some JSON states, replay the same token on
1110+
# an independent fork and compare the resulting state to `draft_fork`.
1111+
accepted_token = int(draft_token_ids.reshape(-1)[0].item())
1112+
replay_fork = original.fork()
1113+
replay_fork.accept_token(accepted_token)
11081114
bm_orig = guided_manager.allocate_batched_bitmap(1)
11091115
guided_manager.fill_bitmap(original, bm_orig, 0)
11101116
bm_fork = guided_manager.allocate_batched_bitmap(1)
11111117
guided_manager.fill_bitmap(draft_fork, bm_fork, 0)
1112-
# fork has advanced by one token; original hasn't
1113-
assert _allowed_ids(bm_orig) != _allowed_ids(bm_fork) or True # may coincide for JSON
1118+
bm_replay = guided_manager.allocate_batched_bitmap(1)
1119+
guided_manager.fill_bitmap(replay_fork, bm_replay, 0)
1120+
assert _allowed_ids(bm_fork) == _allowed_ids(bm_replay)
1121+
# Both original and advanced fork must remain usable for subsequent
1122+
# mask production even if their immediate masks happen to coincide.
11141123

11151124
def test_get_outputs_multi_step_with_fork(self, compiler, tokenizer_info,
11161125
guided_manager):

0 commit comments

Comments
 (0)