From 5b7c62d5d685f0b14859ac26db8d3cacbd50ffa7 Mon Sep 17 00:00:00 2001 From: EchO <117733745+CyberSecurityErial@users.noreply.github.com> Date: Sun, 30 Aug 2026 13:25:26 +0800 Subject: [PATCH] fix(frontend): bound stop strings and regex patterns (#37029) --- python/sglang/srt/sampling/sampling_params.py | 19 +++++++++ .../unit/sampling/test_sampling_params.py | 41 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/python/sglang/srt/sampling/sampling_params.py b/python/sglang/srt/sampling/sampling_params.py index fd7febb4f..da340e56c 100644 --- a/python/sglang/srt/sampling/sampling_params.py +++ b/python/sglang/srt/sampling/sampling_params.py @@ -38,6 +38,9 @@ CustomParamValue = Union[ _SAMPLING_EPS = 1e-6 TOP_K_ALL = 1 << 30 +MAX_STOP_COUNT = 32 +MAX_STOP_REGEX_LEN = 256 +MAX_STOP_REGEX_COUNT = 32 logger = logging.getLogger(__name__) @@ -222,6 +225,11 @@ class SamplingParams(msgspec.Struct, kw_only=True, array_like=True): else: if isinstance(self.stop_strs, str): 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 for stop_str in self.stop_strs: @@ -239,9 +247,20 @@ class SamplingParams(msgspec.Struct, kw_only=True, array_like=True): else: if isinstance(self.stop_regex_strs, str): 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 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, get_max_seq_length(stop_regex) ) diff --git a/test/registered/unit/sampling/test_sampling_params.py b/test/registered/unit/sampling/test_sampling_params.py index 016783c49..8f4b89e55 100644 --- a/test/registered/unit/sampling/test_sampling_params.py +++ b/test/registered/unit/sampling/test_sampling_params.py @@ -14,6 +14,9 @@ import msgspec from sglang.srt.sampling.sampling_params import ( MAX_LEN, + MAX_STOP_COUNT, + MAX_STOP_REGEX_COUNT, + MAX_STOP_REGEX_LEN, TOP_K_ALL, SamplingParams, get_max_seq_length, @@ -344,6 +347,17 @@ class TestSamplingParamsNormalize(CustomTestCase): sp.normalize(tokenizer=tokenizer) 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): """Test that max_len is based on encoded token count, not character count.""" # "ab" encodes to 1 token, "cdef" encodes to 2 tokens @@ -385,6 +399,33 @@ class TestSamplingParamsNormalize(CustomTestCase): sp.normalize(tokenizer=tokenizer) 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):