fix(frontend): bound stop strings and regex patterns (#37029)
This commit is contained in:
@@ -38,6 +38,9 @@ CustomParamValue = Union[
|
|||||||
|
|
||||||
_SAMPLING_EPS = 1e-6
|
_SAMPLING_EPS = 1e-6
|
||||||
TOP_K_ALL = 1 << 30
|
TOP_K_ALL = 1 << 30
|
||||||
|
MAX_STOP_COUNT = 32
|
||||||
|
MAX_STOP_REGEX_LEN = 256
|
||||||
|
MAX_STOP_REGEX_COUNT = 32
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -222,6 +225,11 @@ class SamplingParams(msgspec.Struct, kw_only=True, array_like=True):
|
|||||||
else:
|
else:
|
||||||
if isinstance(self.stop_strs, str):
|
if isinstance(self.stop_strs, str):
|
||||||
self.stop_strs = [self.stop_strs]
|
self.stop_strs = [self.stop_strs]
|
||||||
|
if len(self.stop_strs) > MAX_STOP_COUNT:
|
||||||
|
raise ValueError(
|
||||||
|
f"at most {MAX_STOP_COUNT} stop strings are allowed, "
|
||||||
|
f"got {len(self.stop_strs)}"
|
||||||
|
)
|
||||||
|
|
||||||
stop_str_max_len = 0
|
stop_str_max_len = 0
|
||||||
for stop_str in self.stop_strs:
|
for stop_str in self.stop_strs:
|
||||||
@@ -239,9 +247,20 @@ class SamplingParams(msgspec.Struct, kw_only=True, array_like=True):
|
|||||||
else:
|
else:
|
||||||
if isinstance(self.stop_regex_strs, str):
|
if isinstance(self.stop_regex_strs, str):
|
||||||
self.stop_regex_strs = [self.stop_regex_strs]
|
self.stop_regex_strs = [self.stop_regex_strs]
|
||||||
|
if len(self.stop_regex_strs) > MAX_STOP_REGEX_COUNT:
|
||||||
|
raise ValueError(
|
||||||
|
f"at most {MAX_STOP_REGEX_COUNT} stop_regex patterns are allowed, "
|
||||||
|
f"got {len(self.stop_regex_strs)}"
|
||||||
|
)
|
||||||
|
|
||||||
stop_regex_max_len = 0
|
stop_regex_max_len = 0
|
||||||
for stop_regex in self.stop_regex_strs:
|
for stop_regex in self.stop_regex_strs:
|
||||||
|
stop_regex_len = len(stop_regex.encode("utf-8"))
|
||||||
|
if stop_regex_len > MAX_STOP_REGEX_LEN:
|
||||||
|
raise ValueError(
|
||||||
|
f"stop_regex is {stop_regex_len} bytes, over the "
|
||||||
|
f"{MAX_STOP_REGEX_LEN}-byte limit"
|
||||||
|
)
|
||||||
stop_regex_max_len = max(
|
stop_regex_max_len = max(
|
||||||
stop_regex_max_len, get_max_seq_length(stop_regex)
|
stop_regex_max_len, get_max_seq_length(stop_regex)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import msgspec
|
|||||||
|
|
||||||
from sglang.srt.sampling.sampling_params import (
|
from sglang.srt.sampling.sampling_params import (
|
||||||
MAX_LEN,
|
MAX_LEN,
|
||||||
|
MAX_STOP_COUNT,
|
||||||
|
MAX_STOP_REGEX_COUNT,
|
||||||
|
MAX_STOP_REGEX_LEN,
|
||||||
TOP_K_ALL,
|
TOP_K_ALL,
|
||||||
SamplingParams,
|
SamplingParams,
|
||||||
get_max_seq_length,
|
get_max_seq_length,
|
||||||
@@ -344,6 +347,17 @@ class TestSamplingParamsNormalize(CustomTestCase):
|
|||||||
sp.normalize(tokenizer=tokenizer)
|
sp.normalize(tokenizer=tokenizer)
|
||||||
self.assertEqual(sp.stop_strs, ["stop1", "stop2"])
|
self.assertEqual(sp.stop_strs, ["stop1", "stop2"])
|
||||||
|
|
||||||
|
def test_stop_count_limit(self):
|
||||||
|
tokenizer = self._mock_tokenizer()
|
||||||
|
SamplingParams(stop=["x"] * MAX_STOP_COUNT).normalize(tokenizer)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as cm:
|
||||||
|
SamplingParams(stop=["x"] * (MAX_STOP_COUNT + 1)).normalize(tokenizer)
|
||||||
|
self.assertEqual(
|
||||||
|
str(cm.exception),
|
||||||
|
f"at most {MAX_STOP_COUNT} stop strings are allowed, got {MAX_STOP_COUNT + 1}",
|
||||||
|
)
|
||||||
|
|
||||||
def test_stop_str_max_len_uses_encoded_length(self):
|
def test_stop_str_max_len_uses_encoded_length(self):
|
||||||
"""Test that max_len is based on encoded token count, not character count."""
|
"""Test that max_len is based on encoded token count, not character count."""
|
||||||
# "ab" encodes to 1 token, "cdef" encodes to 2 tokens
|
# "ab" encodes to 1 token, "cdef" encodes to 2 tokens
|
||||||
@@ -385,6 +399,33 @@ class TestSamplingParamsNormalize(CustomTestCase):
|
|||||||
sp.normalize(tokenizer=tokenizer)
|
sp.normalize(tokenizer=tokenizer)
|
||||||
self.assertEqual(sp.stop_regex_max_len, 3)
|
self.assertEqual(sp.stop_regex_max_len, 3)
|
||||||
|
|
||||||
|
def test_stop_regex_count_limit(self):
|
||||||
|
tokenizer = self._mock_tokenizer()
|
||||||
|
SamplingParams(stop_regex=["x"] * MAX_STOP_REGEX_COUNT).normalize(tokenizer)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as cm:
|
||||||
|
SamplingParams(stop_regex=["x"] * (MAX_STOP_REGEX_COUNT + 1)).normalize(
|
||||||
|
tokenizer
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
str(cm.exception),
|
||||||
|
f"at most {MAX_STOP_REGEX_COUNT} stop_regex patterns are allowed, "
|
||||||
|
f"got {MAX_STOP_REGEX_COUNT + 1}",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_stop_regex_byte_length_limit(self):
|
||||||
|
tokenizer = self._mock_tokenizer()
|
||||||
|
pattern = "é" * (MAX_STOP_REGEX_LEN // 2)
|
||||||
|
SamplingParams(stop_regex=pattern).normalize(tokenizer)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as cm:
|
||||||
|
SamplingParams(stop_regex=pattern + "a").normalize(tokenizer)
|
||||||
|
self.assertEqual(
|
||||||
|
str(cm.exception),
|
||||||
|
f"stop_regex is {MAX_STOP_REGEX_LEN + 1} bytes, over the "
|
||||||
|
f"{MAX_STOP_REGEX_LEN}-byte limit",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestSamplingParamsMsgspecStruct(CustomTestCase):
|
class TestSamplingParamsMsgspecStruct(CustomTestCase):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user