Fix stop boundaries for grammar-constrained speculative decoding (#31738)
This commit is contained in:
@@ -1489,11 +1489,6 @@ class Req(ReqDllmMixin):
|
|||||||
self.finished_len = self.sampling_params.max_new_tokens
|
self.finished_len = self.sampling_params.max_new_tokens
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.grammar is not None:
|
|
||||||
if self.grammar.is_terminated():
|
|
||||||
self.finished_reason = FINISH_MATCHED_TOKEN(matched=self.output_ids[-1])
|
|
||||||
return
|
|
||||||
|
|
||||||
new_accepted_tokens = self.output_ids[-new_accepted_len:]
|
new_accepted_tokens = self.output_ids[-new_accepted_len:]
|
||||||
|
|
||||||
# Sanitize out-of-range / NaN token ids before any decode.
|
# Sanitize out-of-range / NaN token ids before any decode.
|
||||||
@@ -1509,6 +1504,10 @@ class Req(ReqDllmMixin):
|
|||||||
if self._check_token_based_finish(new_accepted_tokens):
|
if self._check_token_based_finish(new_accepted_tokens):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.grammar is not None and self.grammar.is_terminated():
|
||||||
|
self.finished_reason = FINISH_MATCHED_TOKEN(matched=self.output_ids[-1])
|
||||||
|
return
|
||||||
|
|
||||||
def reset_for_retract(self):
|
def reset_for_retract(self):
|
||||||
# Increment retraction count before resetting other state. We should not reset this
|
# Increment retraction count before resetting other state. We should not reset this
|
||||||
# since we are tracking the total number of retractions for each request.
|
# since we are tracking the total number of retractions for each request.
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
"""Regression tests for grammar termination during speculative decoding."""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from array import array
|
||||||
|
|
||||||
|
from sglang.srt.managers.schedule_batch import Req
|
||||||
|
from sglang.srt.sampling.sampling_params import SamplingParams
|
||||||
|
from sglang.test.ci.ci_register import register_cpu_ci
|
||||||
|
|
||||||
|
register_cpu_ci(est_time=5, suite="base-a-test-cpu")
|
||||||
|
|
||||||
|
EOS_TOKEN_ID = 2
|
||||||
|
STOP_TOKEN_ID = 17
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeTokenizer:
|
||||||
|
eos_token_id = -1
|
||||||
|
additional_stop_token_ids = None
|
||||||
|
|
||||||
|
|
||||||
|
class _TerminatedGrammar:
|
||||||
|
@staticmethod
|
||||||
|
def is_terminated():
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _make_req(stop_token_ids=None):
|
||||||
|
sampling_params = SamplingParams(
|
||||||
|
max_new_tokens=1_000,
|
||||||
|
stop_token_ids=stop_token_ids,
|
||||||
|
)
|
||||||
|
sampling_params.normalize(tokenizer=_FakeTokenizer())
|
||||||
|
req = Req(
|
||||||
|
rid="grammar-stop",
|
||||||
|
origin_input_text="",
|
||||||
|
origin_input_ids=array("q", [0]),
|
||||||
|
sampling_params=sampling_params,
|
||||||
|
eos_token_ids={EOS_TOKEN_ID},
|
||||||
|
vocab_size=100,
|
||||||
|
)
|
||||||
|
req.tokenizer = _FakeTokenizer()
|
||||||
|
req.grammar = _TerminatedGrammar()
|
||||||
|
req.output_ids = array("q", [11, 13, STOP_TOKEN_ID, EOS_TOKEN_ID])
|
||||||
|
return req
|
||||||
|
|
||||||
|
|
||||||
|
class TestGrammarStopSpeculative(unittest.TestCase):
|
||||||
|
def test_requested_stop_token_wins_over_trailing_eos(self):
|
||||||
|
req = _make_req(stop_token_ids=[STOP_TOKEN_ID])
|
||||||
|
|
||||||
|
req.update_finish_state(new_accepted_len=4)
|
||||||
|
|
||||||
|
self.assertEqual(req.finished_reason.matched, STOP_TOKEN_ID)
|
||||||
|
self.assertEqual(req.finished_len, 3)
|
||||||
|
self.assertEqual(list(req.output_ids_through_stop), [11, 13, STOP_TOKEN_ID])
|
||||||
|
|
||||||
|
def test_tokenizer_stop_token_wins_over_trailing_eos(self):
|
||||||
|
req = _make_req()
|
||||||
|
req.tokenizer.additional_stop_token_ids = {STOP_TOKEN_ID}
|
||||||
|
|
||||||
|
req.update_finish_state(new_accepted_len=4)
|
||||||
|
|
||||||
|
self.assertEqual(req.finished_reason.matched, STOP_TOKEN_ID)
|
||||||
|
self.assertEqual(req.finished_len, 3)
|
||||||
|
self.assertEqual(list(req.output_ids_through_stop), [11, 13, STOP_TOKEN_ID])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user