migrate CPU-only unit tests from openai_server to unit/ (#22965)
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import unittest
|
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.srt.utils import kill_process_tree
|
||||||
from sglang.test.ci.ci_register import register_amd_ci, register_cuda_ci
|
from sglang.test.ci.ci_register import register_amd_ci, register_cuda_ci
|
||||||
from sglang.test.kits.matched_stop_kit import MatchedStopMixin
|
from sglang.test.kits.matched_stop_kit import MatchedStopMixin
|
||||||
@@ -32,53 +31,5 @@ class TestMatchedStop(CustomTestCase, MatchedStopMixin):
|
|||||||
kill_process_tree(cls.process.pid)
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -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()
|
||||||
+2
-3
@@ -28,10 +28,9 @@ from sglang.srt.entrypoints.openai.protocol import (
|
|||||||
ModelList,
|
ModelList,
|
||||||
UsageInfo,
|
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_cpu_ci(est_time=2, suite="stage-a-test-cpu")
|
||||||
register_amd_ci(est_time=10, suite="stage-b-test-1-gpu-small-amd")
|
|
||||||
|
|
||||||
|
|
||||||
class TestModelCard(unittest.TestCase):
|
class TestModelCard(unittest.TestCase):
|
||||||
+26
-38
@@ -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:
|
Run with either:
|
||||||
python tests/test_serving_chat_unit.py -v
|
python tests/test_serving_chat_unit.py -v
|
||||||
or
|
or
|
||||||
python -m unittest discover -s tests -p "test_*unit.py" -v
|
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 json
|
||||||
import unittest
|
import unittest
|
||||||
import uuid
|
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.managers.io_struct import GenerateReqInput
|
||||||
from sglang.srt.utils import get_or_create_event_loop
|
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_cpu_ci(est_time=8, suite="stage-a-test-cpu")
|
||||||
register_amd_ci(est_time=10, suite="stage-b-test-1-gpu-small-amd")
|
|
||||||
|
|
||||||
|
|
||||||
class _MockTokenizerManager:
|
class _MockTokenizerManager:
|
||||||
@@ -670,45 +673,30 @@ class ServingChatTestCase(unittest.TestCase):
|
|||||||
def test_dpsk_v32_encoding_path(self):
|
def test_dpsk_v32_encoding_path(self):
|
||||||
"""Test DeepSeek V3.2 encoding path detection and application."""
|
"""Test DeepSeek V3.2 encoding path detection and application."""
|
||||||
from sglang.srt.managers.template_manager import TemplateManager
|
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")
|
# Only mock the fields that _use_dpsk_v32_encoding() actually reads:
|
||||||
port_args = PortArgs.init_new(server_args)
|
# tokenizer.chat_template and hf_config.architectures
|
||||||
|
tm = _MockTokenizerManager()
|
||||||
|
|
||||||
# Use mocks for TokenizerManager components to avoid full initialization
|
mock_hf_config = Mock()
|
||||||
with patch(
|
mock_hf_config.architectures = ["DeepseekV32ForCausalLM"]
|
||||||
"sglang.srt.managers.tokenizer_manager.TokenizerManager"
|
tm.model_config.hf_config = mock_hf_config
|
||||||
) 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
|
# Case 1: No chat template + DeepSeek V3.2 arch -> should use dpsk encoding
|
||||||
mock_hf_config = Mock()
|
tm.tokenizer.chat_template = None
|
||||||
mock_hf_config.architectures = ["DeepseekV32ForCausalLM"]
|
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
|
# Case 3: Not DeepSeek V3.2 architecture -> should NOT use dpsk encoding
|
||||||
tokenizer_manager.tokenizer = Mock()
|
tm.tokenizer.chat_template = None
|
||||||
tokenizer_manager.tokenizer.chat_template = None
|
mock_hf_config.architectures = ["LlamaForCausalLM"]
|
||||||
|
serving_chat = OpenAIServingChat(tm, TemplateManager())
|
||||||
serving_chat = OpenAIServingChat(tokenizer_manager, TemplateManager())
|
self.assertFalse(serving_chat.use_dpsk_v32_encoding)
|
||||||
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)
|
|
||||||
|
|
||||||
def test_streaming_abort_yields_error(self):
|
def test_streaming_abort_yields_error(self):
|
||||||
"""Test that an abort finish reason during streaming correctly yields an error and stops."""
|
"""Test that an abort finish reason during streaming correctly yields an error and stops."""
|
||||||
+6
-3
@@ -4,6 +4,10 @@ Run with:
|
|||||||
python -m unittest tests.test_serving_completions_unit -v
|
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 json
|
||||||
import unittest
|
import unittest
|
||||||
from http import HTTPStatus
|
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.entrypoints.openai.serving_completions import OpenAIServingCompletion
|
||||||
from sglang.srt.managers.tokenizer_manager import TokenizerManager
|
from sglang.srt.managers.tokenizer_manager import TokenizerManager
|
||||||
from sglang.srt.utils import get_or_create_event_loop
|
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_cpu_ci(est_time=8, suite="stage-a-test-cpu")
|
||||||
register_amd_ci(est_time=10, suite="stage-b-test-1-gpu-small-amd")
|
|
||||||
|
|
||||||
|
|
||||||
class _MockTemplateManager:
|
class _MockTemplateManager:
|
||||||
Reference in New Issue
Block a user