diff --git a/test/registered/openai_server/validation/test_matched_stop.py b/test/registered/openai_server/validation/test_matched_stop.py index da8ec59cf..c6402ab21 100644 --- a/test/registered/openai_server/validation/test_matched_stop.py +++ b/test/registered/openai_server/validation/test_matched_stop.py @@ -1,6 +1,5 @@ import unittest -from sglang.srt.sampling.sampling_params import MAX_LEN, get_max_seq_length from sglang.srt.utils import kill_process_tree from sglang.test.ci.ci_register import register_amd_ci, register_cuda_ci from sglang.test.kits.matched_stop_kit import MatchedStopMixin @@ -32,53 +31,5 @@ class TestMatchedStop(CustomTestCase, MatchedStopMixin): kill_process_tree(cls.process.pid) -class TestRegexPatternMaxLength(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.regex_str_to_max_len = { - "((ab|cd(e|f){2}){3,5}g|hij)*k": MAX_LEN, - # - '*' → infinite tokens need to be stored - "abc*?k": MAX_LEN, - # - '*?' → infinite tokens still need to be stored even if lazy matching used - "^spec(foo|at)$": 7, - # - '^' and '$' don't add any characters to the max length - # "spec" → 4 - # "(foo|at)" → max(3, 2) = 3 - # Whole regex = 7 - "(a(bca|de(fg|hi){2,3})j){2}kl": 22, - # - Innermost alt: "fg" vs "hi" → 2 - # - Repeat {2,3}: max = 3 * 2 = 6 - # - Inner group "de(...)": 2 (for "de") + 6 = 8. - # - "bca" or "de(...)" → max(3, 8) = 8 - # - Whole group: "a" (1) + group (8) + "j"(1) = 10 - # - Repeat {2} → 20 - # - Add "kl"(2) → 22 - "(foo(bar|baz(qux){1,2}))|(x(yz){5,10})": 21, - # Branch 1: - # "foo"(3) + max("bar"(3), "baz"(3)+"qux"{2} = 3 + 6 = 9) = 3 + 9 = 12 - # Branch 2: - # "x"(1) + "yz"{10} = 1 + 20 =21 - # Whole regex = max(12, 21) = 21 - "(((a|bc){1,3}(d(e|f){2}|gh){2,4})|(ijk|lmp(no|p){3})){5}": 90, - # Branch A: - # (a|bc){1,3} → max = 3 * 2 = 6 - # Inside: d(e|f){2} = 1 + 2 * 1 = 3 vs gh = 2 → max = 3 - # Repeat {2,4} → 4 * 3 = 12 - # Branch A total = 18 - # Branch B: - # "ijk"(3) vs "lmp(no|p){3}" = 3 + 3 * max(2, 1) = 3 + 6 = 9 → max = 9 - # Branch B total = 9 - # Whole outer alt = max(18, 9) = 18 - # Repeat {5} → 90 - } - - def test_get_max_length(self): - for regex_str, max_len in self.regex_str_to_max_len.items(): - if max_len == MAX_LEN: - self.assertGreaterEqual(get_max_seq_length(regex_str), MAX_LEN) - else: - self.assertEqual(get_max_seq_length(regex_str), max_len) - - if __name__ == "__main__": unittest.main() diff --git a/test/registered/unit/entrypoints/openai/test_matched_stop.py b/test/registered/unit/entrypoints/openai/test_matched_stop.py new file mode 100644 index 000000000..d3d8c65d1 --- /dev/null +++ b/test/registered/unit/entrypoints/openai/test_matched_stop.py @@ -0,0 +1,58 @@ +import unittest + +from sglang.srt.sampling.sampling_params import MAX_LEN, get_max_seq_length +from sglang.test.ci.ci_register import register_cpu_ci + +register_cpu_ci(est_time=2, suite="stage-a-test-cpu") + + +class TestRegexPatternMaxLength(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.regex_str_to_max_len = { + "((ab|cd(e|f){2}){3,5}g|hij)*k": MAX_LEN, + # - '*' -> infinite tokens need to be stored + "abc*?k": MAX_LEN, + # - '*?' -> infinite tokens still need to be stored even if lazy matching used + "^spec(foo|at)$": 7, + # - '^' and '$' don't add any characters to the max length + # "spec" -> 4 + # "(foo|at)" -> max(3, 2) = 3 + # Whole regex = 7 + "(a(bca|de(fg|hi){2,3})j){2}kl": 22, + # - Innermost alt: "fg" vs "hi" -> 2 + # - Repeat {2,3}: max = 3 * 2 = 6 + # - Inner group "de(...)": 2 (for "de") + 6 = 8. + # - "bca" or "de(...)" -> max(3, 8) = 8 + # - Whole group: "a" (1) + group (8) + "j"(1) = 10 + # - Repeat {2} -> 20 + # - Add "kl"(2) -> 22 + "(foo(bar|baz(qux){1,2}))|(x(yz){5,10})": 21, + # Branch 1: + # "foo"(3) + max("bar"(3), "baz"(3)+"qux"{2} = 3 + 6 = 9) = 3 + 9 = 12 + # Branch 2: + # "x"(1) + "yz"{10} = 1 + 20 =21 + # Whole regex = max(12, 21) = 21 + "(((a|bc){1,3}(d(e|f){2}|gh){2,4})|(ijk|lmp(no|p){3})){5}": 90, + # Branch A: + # (a|bc){1,3} -> max = 3 * 2 = 6 + # Inside: d(e|f){2} = 1 + 2 * 1 = 3 vs gh = 2 -> max = 3 + # Repeat {2,4} -> 4 * 3 = 12 + # Branch A total = 18 + # Branch B: + # "ijk"(3) vs "lmp(no|p){3}" = 3 + 3 * max(2, 1) = 3 + 6 = 9 -> max = 9 + # Branch B total = 9 + # Whole outer alt = max(18, 9) = 18 + # Repeat {5} -> 90 + } + + def test_get_max_length(self): + for regex_str, max_len in self.regex_str_to_max_len.items(): + if max_len == MAX_LEN: + self.assertGreaterEqual(get_max_seq_length(regex_str), MAX_LEN) + else: + self.assertEqual(get_max_seq_length(regex_str), max_len) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/registered/openai_server/basic/test_protocol.py b/test/registered/unit/entrypoints/openai/test_protocol.py similarity index 98% rename from test/registered/openai_server/basic/test_protocol.py rename to test/registered/unit/entrypoints/openai/test_protocol.py index b707f35bf..ffe2c157f 100644 --- a/test/registered/openai_server/basic/test_protocol.py +++ b/test/registered/unit/entrypoints/openai/test_protocol.py @@ -28,10 +28,9 @@ from sglang.srt.entrypoints.openai.protocol import ( ModelList, UsageInfo, ) -from sglang.test.ci.ci_register import register_amd_ci, register_cuda_ci +from sglang.test.ci.ci_register import register_cpu_ci -register_cuda_ci(est_time=2, suite="stage-b-test-1-gpu-small") -register_amd_ci(est_time=10, suite="stage-b-test-1-gpu-small-amd") +register_cpu_ci(est_time=2, suite="stage-a-test-cpu") class TestModelCard(unittest.TestCase): diff --git a/test/registered/openai_server/basic/test_serving_chat.py b/test/registered/unit/entrypoints/openai/test_serving_chat.py similarity index 94% rename from test/registered/openai_server/basic/test_serving_chat.py rename to test/registered/unit/entrypoints/openai/test_serving_chat.py index 6dd0cdea7..884a221a8 100644 --- a/test/registered/openai_server/basic/test_serving_chat.py +++ b/test/registered/unit/entrypoints/openai/test_serving_chat.py @@ -1,11 +1,15 @@ """ -Unit-tests for OpenAIServingChat — rewritten to use only the std-lib 'unittest'. +Unit-tests for OpenAIServingChat -- rewritten to use only the std-lib 'unittest'. Run with either: python tests/test_serving_chat_unit.py -v or python -m unittest discover -s tests -p "test_*unit.py" -v """ +from sglang.test.test_utils import maybe_stub_sgl_kernel + +maybe_stub_sgl_kernel() # must precede any import that pulls in sgl_kernel + import json import unittest import uuid @@ -25,10 +29,9 @@ from sglang.srt.entrypoints.openai.serving_chat import ( ) from sglang.srt.managers.io_struct import GenerateReqInput from sglang.srt.utils import get_or_create_event_loop -from sglang.test.ci.ci_register import register_amd_ci, register_cuda_ci +from sglang.test.ci.ci_register import register_cpu_ci -register_cuda_ci(est_time=8, suite="stage-b-test-1-gpu-small") -register_amd_ci(est_time=10, suite="stage-b-test-1-gpu-small-amd") +register_cpu_ci(est_time=8, suite="stage-a-test-cpu") class _MockTokenizerManager: @@ -670,45 +673,30 @@ class ServingChatTestCase(unittest.TestCase): def test_dpsk_v32_encoding_path(self): """Test DeepSeek V3.2 encoding path detection and application.""" from sglang.srt.managers.template_manager import TemplateManager - from sglang.srt.server_args import PortArgs, ServerArgs - server_args = ServerArgs(model_path="deepseek-ai/DeepSeek-V3.2") - port_args = PortArgs.init_new(server_args) + # Only mock the fields that _use_dpsk_v32_encoding() actually reads: + # tokenizer.chat_template and hf_config.architectures + tm = _MockTokenizerManager() - # Use mocks for TokenizerManager components to avoid full initialization - with patch( - "sglang.srt.managers.tokenizer_manager.TokenizerManager" - ) as MockTokenizerManager: - tokenizer_manager = MockTokenizerManager(server_args, port_args) - tokenizer_manager.server_args = server_args - tokenizer_manager.model_config = Mock() - tokenizer_manager.model_config.get_default_sampling_params.return_value = ( - None - ) + mock_hf_config = Mock() + mock_hf_config.architectures = ["DeepseekV32ForCausalLM"] + tm.model_config.hf_config = mock_hf_config - # Mock hf_config - mock_hf_config = Mock() - mock_hf_config.architectures = ["DeepseekV32ForCausalLM"] + # Case 1: No chat template + DeepSeek V3.2 arch -> should use dpsk encoding + tm.tokenizer.chat_template = None + serving_chat = OpenAIServingChat(tm, TemplateManager()) + self.assertTrue(serving_chat.use_dpsk_v32_encoding) - tokenizer_manager.model_config.hf_config = mock_hf_config + # Case 2: Chat template exists -> should NOT use dpsk encoding + tm.tokenizer.chat_template = "some template" + serving_chat = OpenAIServingChat(tm, TemplateManager()) + self.assertFalse(serving_chat.use_dpsk_v32_encoding) - # Case 1: No chat template in tokenizer -> should use dpsk encoding - tokenizer_manager.tokenizer = Mock() - tokenizer_manager.tokenizer.chat_template = None - - serving_chat = OpenAIServingChat(tokenizer_manager, TemplateManager()) - self.assertTrue(serving_chat.use_dpsk_v32_encoding) - - # Case 2: Chat template exists -> should NOT use dpsk encoding - tokenizer_manager.tokenizer.chat_template = "some template" - serving_chat = OpenAIServingChat(tokenizer_manager, TemplateManager()) - self.assertFalse(serving_chat.use_dpsk_v32_encoding) - - # Case 3: Not DeepSeek V3.2 architecture -> should NOT use dpsk encoding - tokenizer_manager.tokenizer.chat_template = None - mock_hf_config.architectures = ["LlamaForCausalLM"] - serving_chat = OpenAIServingChat(tokenizer_manager, TemplateManager()) - self.assertFalse(serving_chat.use_dpsk_v32_encoding) + # Case 3: Not DeepSeek V3.2 architecture -> should NOT use dpsk encoding + tm.tokenizer.chat_template = None + mock_hf_config.architectures = ["LlamaForCausalLM"] + serving_chat = OpenAIServingChat(tm, TemplateManager()) + self.assertFalse(serving_chat.use_dpsk_v32_encoding) def test_streaming_abort_yields_error(self): """Test that an abort finish reason during streaming correctly yields an error and stops.""" diff --git a/test/registered/openai_server/basic/test_serving_completions.py b/test/registered/unit/entrypoints/openai/test_serving_completions.py similarity index 97% rename from test/registered/openai_server/basic/test_serving_completions.py rename to test/registered/unit/entrypoints/openai/test_serving_completions.py index 1ded89c39..7b19b59ca 100644 --- a/test/registered/openai_server/basic/test_serving_completions.py +++ b/test/registered/unit/entrypoints/openai/test_serving_completions.py @@ -4,6 +4,10 @@ Run with: python -m unittest tests.test_serving_completions_unit -v """ +from sglang.test.test_utils import maybe_stub_sgl_kernel + +maybe_stub_sgl_kernel() # must precede any import that pulls in sgl_kernel + import json import unittest from http import HTTPStatus @@ -16,10 +20,9 @@ from sglang.srt.entrypoints.openai.protocol import CompletionRequest from sglang.srt.entrypoints.openai.serving_completions import OpenAIServingCompletion from sglang.srt.managers.tokenizer_manager import TokenizerManager from sglang.srt.utils import get_or_create_event_loop -from sglang.test.ci.ci_register import register_amd_ci, register_cuda_ci +from sglang.test.ci.ci_register import register_cpu_ci -register_cuda_ci(est_time=8, suite="stage-b-test-1-gpu-small") -register_amd_ci(est_time=10, suite="stage-b-test-1-gpu-small-amd") +register_cpu_ci(est_time=8, suite="stage-a-test-cpu") class _MockTemplateManager: