[CI] Share VLM engines and prune launch matrices on the per-commit H100/H200 suites (#33944)
This commit is contained in:
@@ -24,7 +24,7 @@ from sglang.test.test_utils import (
|
||||
)
|
||||
|
||||
# CI Registration — large suite to fit the integration test's server startup.
|
||||
register_cuda_ci(est_time=79, stage="base-b", runner_config="1-gpu-large")
|
||||
register_cuda_ci(est_time=60, stage="base-b", runner_config="1-gpu-large")
|
||||
register_amd_ci(est_time=200, suite="stage-c-test-large-8-gpu-amd-mi35x")
|
||||
|
||||
|
||||
@@ -388,7 +388,7 @@ class TestBreakableCudaGraph(CustomTestCase):
|
||||
base_url=self.base_url,
|
||||
model=self.model,
|
||||
eval_name="mgsm_en",
|
||||
num_examples=1319,
|
||||
num_examples=200,
|
||||
num_threads=1024,
|
||||
)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ from sglang.test.lora_utils import (
|
||||
)
|
||||
from sglang.test.test_utils import CustomTestCase
|
||||
|
||||
register_cuda_ci(est_time=48, stage="base-b", runner_config="1-gpu-large")
|
||||
register_cuda_ci(est_time=380, stage="base-b", runner_config="1-gpu-large")
|
||||
register_amd_ci(est_time=75, suite="stage-b-test-1-gpu-small-amd")
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ from sglang.test.test_utils import (
|
||||
)
|
||||
|
||||
# CI Registration
|
||||
register_cuda_ci(est_time=240, stage="base-b", runner_config="1-gpu-large")
|
||||
register_cuda_ci(est_time=100, stage="base-b", runner_config="1-gpu-large")
|
||||
|
||||
STDOUT_FILENAME = "post_capture_kv_sizing_stdout.log"
|
||||
STDERR_FILENAME = "post_capture_kv_sizing_stderr.log"
|
||||
@@ -88,7 +88,7 @@ class TestPostCaptureKVSizing(CustomTestCase):
|
||||
base_url=self.base_url,
|
||||
model=self.model,
|
||||
eval_name="gsm8k",
|
||||
num_examples=500,
|
||||
num_examples=200,
|
||||
num_threads=1024,
|
||||
)
|
||||
metrics = run_eval(args)
|
||||
|
||||
@@ -31,7 +31,7 @@ from sglang.test.test_utils import (
|
||||
popen_launch_server,
|
||||
)
|
||||
|
||||
register_cuda_ci(est_time=600, stage="base-b", runner_config="1-gpu-large")
|
||||
register_cuda_ci(est_time=250, stage="base-b", runner_config="1-gpu-large")
|
||||
|
||||
# Defaults to the HF `test` revision; override MODEL/REVISION to point at a
|
||||
# local checkpoint. Empty REVISION drops the flag (for local paths).
|
||||
@@ -7,13 +7,11 @@ from sglang.test.kits.eval_accuracy_kit import GSM8KMixin
|
||||
from sglang.test.kits.spec_decoding_kit import SpecDecodingMixin
|
||||
from sglang.test.server_fixtures.default_fixture import DefaultServerBase
|
||||
|
||||
register_cuda_ci(est_time=350, stage="base-c", runner_config="8-gpu-h200")
|
||||
register_cuda_ci(est_time=200, stage="base-c", runner_config="8-gpu-h200")
|
||||
|
||||
|
||||
class TestMiMoV2Flash(GSM8KMixin, SpecDecodingMixin, DefaultServerBase):
|
||||
gsm8k_accuracy_thres = 0.75
|
||||
gsm8k_num_questions = 1319
|
||||
gsm8k_num_threads = 1319
|
||||
model = "XiaomiMiMo/MiMo-V2-Flash"
|
||||
|
||||
other_args = [
|
||||
|
||||
@@ -14,7 +14,7 @@ from sglang.test.test_utils import (
|
||||
write_github_step_summary,
|
||||
)
|
||||
|
||||
register_cuda_ci(est_time=250, stage="base-c", runner_config="8-gpu-h200")
|
||||
register_cuda_ci(est_time=160, stage="base-c", runner_config="8-gpu-h200")
|
||||
|
||||
MINIMAX_M25_MODEL_PATH = "MiniMaxAI/MiniMax-M2.5"
|
||||
|
||||
@@ -53,8 +53,8 @@ class TestMiniMaxM25Basic(CustomTestCase):
|
||||
args = SimpleNamespace(
|
||||
num_shots=20,
|
||||
data_path=None,
|
||||
num_questions=1400,
|
||||
parallel=1400,
|
||||
num_questions=200,
|
||||
parallel=200,
|
||||
max_new_tokens=512,
|
||||
host="http://127.0.0.1",
|
||||
port=int(self.base_url.split(":")[-1]),
|
||||
|
||||
@@ -7,7 +7,7 @@ from sglang.test.kits.kl_divergence_kit import KLDivergenceMixin
|
||||
from sglang.test.kits.prefix_cache_branching_kit import PrefixCacheBranchingMixin
|
||||
from sglang.test.server_fixtures.default_fixture import DefaultServerBase
|
||||
|
||||
register_cuda_ci(est_time=430, stage="base-c", runner_config="4-gpu-h100")
|
||||
register_cuda_ci(est_time=290, stage="base-c", runner_config="4-gpu-h100")
|
||||
|
||||
QWEN3_NEXT_MODEL = "Qwen/Qwen3-Next-80B-A3B-Instruct"
|
||||
|
||||
@@ -69,6 +69,11 @@ class TestQwen3NextMTPTopk(
|
||||
]
|
||||
|
||||
|
||||
@unittest.skip(
|
||||
"Manual-only: topk == 1 is covered by TestQwen3NextMTPLazyV2 and extra_buffer "
|
||||
"by TestQwen3NextMTPTopk. Kept runnable locally for the plain (topk=1, "
|
||||
"extra_buffer) baseline."
|
||||
)
|
||||
class TestQwen3NextMTPV2(GSM8KMixin, KLDivergenceMixin, DefaultServerBase):
|
||||
model = QWEN3_NEXT_MODEL
|
||||
gsm8k_accuracy_thres = 0.93
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
"""Per-commit streaming-session tests on a hybrid-SWA model.
|
||||
|
||||
Baseline + large-page retract + abort-leak repro stay per-commit; the
|
||||
mixed-chunk retract variant lives in test_streaming_session_swa_extra.py.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
from sglang.test.ci.ci_register import register_cuda_ci
|
||||
@@ -9,20 +15,12 @@ from sglang.test.server_fixtures.streaming_session_fixture import (
|
||||
ABORT_REPRO_CHUNKED_PREFILL_SIZE,
|
||||
ABORT_REPRO_CONTEXT_LEN,
|
||||
ABORT_REPRO_PAGE_SIZE,
|
||||
SWA_COMMON_ARGS,
|
||||
SWA_MODEL,
|
||||
StreamingSessionServerBase,
|
||||
)
|
||||
|
||||
register_cuda_ci(est_time=519, stage="base-b", runner_config="1-gpu-large")
|
||||
|
||||
|
||||
SWA_MODEL = "openai/gpt-oss-20b"
|
||||
|
||||
# Common gpt-oss-20b launch args. Matches TestSessionLatency/TestSWARadixCacheKL.
|
||||
SWA_COMMON_ARGS = [
|
||||
"--mem-fraction-static",
|
||||
"0.70",
|
||||
"--cuda-graph-backend-prefill=disabled",
|
||||
]
|
||||
register_cuda_ci(est_time=390, stage="base-b", runner_config="1-gpu-large")
|
||||
|
||||
|
||||
class TestStreamingSessionSWA(StreamingSessionServerBase, StreamingSessionKitMixin):
|
||||
@@ -48,21 +46,6 @@ class TestStreamingSessionSWARetractLargePage(
|
||||
env_overrides = [("SGLANG_TEST_RETRACT", True)]
|
||||
|
||||
|
||||
class TestStreamingSessionSWARetractMixedChunk(
|
||||
StreamingSessionServerBase, StreamingSessionKitMixin
|
||||
):
|
||||
"""SWA under retract decode with --enable-mixed-chunk."""
|
||||
|
||||
model = SWA_MODEL
|
||||
extra_args = [
|
||||
"--chunked-prefill-size",
|
||||
"128",
|
||||
"--enable-mixed-chunk",
|
||||
*SWA_COMMON_ARGS,
|
||||
]
|
||||
env_overrides = [("SGLANG_TEST_RETRACT", True)]
|
||||
|
||||
|
||||
class TestStreamingSessionSWAAbortLeakRepro(
|
||||
StreamingSessionServerBase, AbortLeakReproKitMixin
|
||||
):
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Label-gated SWA streaming-session variants.
|
||||
|
||||
CUDA-only: gpt-oss-20b is not part of the AMD streaming-session coverage.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
from sglang.test.ci.ci_register import register_cuda_ci
|
||||
from sglang.test.kits.streaming_session_kit import StreamingSessionKitMixin
|
||||
from sglang.test.server_fixtures.streaming_session_fixture import (
|
||||
SWA_COMMON_ARGS,
|
||||
SWA_MODEL,
|
||||
StreamingSessionServerBase,
|
||||
)
|
||||
|
||||
register_cuda_ci(est_time=130, stage="extra-a", runner_config="1-gpu-large")
|
||||
|
||||
|
||||
class TestStreamingSessionSWARetractMixedChunk(
|
||||
StreamingSessionServerBase, StreamingSessionKitMixin
|
||||
):
|
||||
"""SWA under retract decode with --enable-mixed-chunk."""
|
||||
|
||||
model = SWA_MODEL
|
||||
extra_args = [
|
||||
"--chunked-prefill-size",
|
||||
"128",
|
||||
"--enable-mixed-chunk",
|
||||
*SWA_COMMON_ARGS,
|
||||
]
|
||||
env_overrides = [("SGLANG_TEST_RETRACT", True)]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -20,7 +20,7 @@ from sglang.test.vlm_utils import (
|
||||
terminate_and_kill_process_tree,
|
||||
)
|
||||
|
||||
register_cuda_ci(est_time=780, stage="base-b", runner_config="1-gpu-large")
|
||||
register_cuda_ci(est_time=560, stage="base-b", runner_config="1-gpu-large")
|
||||
|
||||
|
||||
class TestLlavaServer(ImageOpenAITestMixin):
|
||||
@@ -43,17 +43,10 @@ class TestQwen3VLServer(ImageOpenAITestMixin, VideoOpenAITestMixin):
|
||||
extra_args = ["--cuda-graph-max-bs-decode=4"]
|
||||
|
||||
|
||||
class TestQwen3OmniServer(OmniOpenAITestMixin):
|
||||
model = "Qwen/Qwen3-Omni-30B-A3B-Instruct"
|
||||
extra_args = [ # workaround to fit into H100
|
||||
"--mem-fraction-static=0.90",
|
||||
"--disable-cuda-graph",
|
||||
"--disable-fast-image-processor",
|
||||
"--grammar-backend=none",
|
||||
]
|
||||
|
||||
|
||||
class TestQwen2VLContextLengthServer(CustomTestCase):
|
||||
# --context-length 300 is calibrated to this model's mm-token expansion:
|
||||
# it must sit above the warmup image's expanded length but below the test
|
||||
# image's. A cheaper VLM needs the bound recalibrated, not just swapped.
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.model = "Qwen/Qwen2-VL-7B-Instruct"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Label-gated vision/omni server launches too expensive for the per-commit
|
||||
budget; the per-commit set lives in test_vision_openai_server_a.py."""
|
||||
|
||||
import unittest
|
||||
|
||||
from sglang.test.ci.ci_register import register_cuda_ci
|
||||
from sglang.test.vlm_utils import OmniOpenAITestMixin
|
||||
|
||||
register_cuda_ci(est_time=180, stage="extra-a", runner_config="1-gpu-large")
|
||||
|
||||
|
||||
class TestQwen3OmniServer(OmniOpenAITestMixin):
|
||||
model = "Qwen/Qwen3-Omni-30B-A3B-Instruct"
|
||||
extra_args = [ # workaround to fit into H100
|
||||
"--mem-fraction-static=0.90",
|
||||
"--disable-cuda-graph",
|
||||
"--disable-fast-image-processor",
|
||||
"--grammar-backend=none",
|
||||
]
|
||||
|
||||
|
||||
# Delete the mixin so it is not collected as a test case in its own right.
|
||||
del OmniOpenAITestMixin
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -37,8 +37,9 @@ from sglang.srt.entrypoints.openai.protocol import ChatCompletionRequest
|
||||
from sglang.srt.parser.conversation import generate_chat_conv
|
||||
from sglang.srt.utils.common import is_cuda, is_xpu
|
||||
from sglang.srt.utils.hf_transformers_utils import _fix_added_tokens_encoding
|
||||
from sglang.test.test_utils import CustomTestCase
|
||||
|
||||
register_cuda_ci(est_time=747, stage="base-b", runner_config="1-gpu-large")
|
||||
register_cuda_ci(est_time=300, stage="base-b", runner_config="1-gpu-large")
|
||||
|
||||
IMAGE_MAN_IRONING_URL = "https://raw.githubusercontent.com/sgl-project/sgl-test-files/refs/heads/main/images/man_ironing_on_back_of_suv.png"
|
||||
IMAGE_SGL_LOGO_URL = "https://raw.githubusercontent.com/sgl-project/sgl-test-files/refs/heads/main/images/sgl_logo.png"
|
||||
@@ -52,6 +53,7 @@ class VLMInputTestBase:
|
||||
chat_template = None
|
||||
processor = None
|
||||
visual = None # Should be a callable for precomputed embeddings
|
||||
engine = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@@ -76,25 +78,34 @@ class VLMInputTestBase:
|
||||
)
|
||||
_fix_added_tokens_encoding(cls.processor.tokenizer)
|
||||
cls._init_visual()
|
||||
cls._start_engine()
|
||||
|
||||
@classmethod
|
||||
def _init_visual(cls):
|
||||
"""Override in subclass to set up cls.visual as a callable for precomputed embeddings."""
|
||||
raise NotImplementedError
|
||||
|
||||
def setUp(self):
|
||||
self.engine = Engine(
|
||||
model_path=self.model_path,
|
||||
chat_template=self.chat_template,
|
||||
device=self.device.type,
|
||||
@classmethod
|
||||
def _start_engine(cls):
|
||||
# One engine per class: every test only reads through it. The tests are
|
||||
# sync rather than async because the tokenizer manager pins handle_loop
|
||||
# to the loop of the first request, so a per-test loop would strand it.
|
||||
cls.engine = Engine(
|
||||
model_path=cls.model_path,
|
||||
chat_template=cls.chat_template,
|
||||
device=cls.device.type,
|
||||
mem_fraction_static=0.8,
|
||||
enable_multimodal=True,
|
||||
disable_cuda_graph=True,
|
||||
trust_remote_code=True,
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
self.engine.shutdown()
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
# CustomTestCase runs tearDownClass even when setUpClass raised.
|
||||
if cls.engine is not None:
|
||||
cls.engine.shutdown()
|
||||
cls.engine = None
|
||||
|
||||
def verify_response(self, output):
|
||||
# The goal is to check that the model roughly understands:
|
||||
@@ -157,25 +168,25 @@ class VLMInputTestBase:
|
||||
|
||||
return inputs, text
|
||||
|
||||
async def test_accepts_image(self):
|
||||
def test_accepts_image(self):
|
||||
req = self.get_completion_request()
|
||||
conv = generate_chat_conv(req, template_name=self.chat_template)
|
||||
text = conv.get_prompt()
|
||||
output = await self.engine.async_generate(
|
||||
output = self.engine.generate(
|
||||
prompt=text,
|
||||
image_data=self.main_image,
|
||||
sampling_params=dict(temperature=0.0, max_new_tokens=512),
|
||||
)
|
||||
self.verify_response(output)
|
||||
|
||||
async def test_accepts_precomputed_embeddings(self):
|
||||
def test_accepts_precomputed_embeddings(self):
|
||||
req = self.get_completion_request()
|
||||
processor_output, _ = self.get_processor_output(req=req)
|
||||
|
||||
with torch.inference_mode():
|
||||
precomputed_embeddings = self.__class__.visual(processor_output)
|
||||
|
||||
output = await self.engine.async_generate(
|
||||
output = self.engine.generate(
|
||||
input_ids=processor_output["input_ids"][0].detach().cpu().tolist(),
|
||||
image_data=[
|
||||
self._precomputed_image_data(processor_output, precomputed_embeddings)
|
||||
@@ -184,10 +195,10 @@ class VLMInputTestBase:
|
||||
)
|
||||
self.verify_response(output)
|
||||
|
||||
async def test_accepts_processor_output(self):
|
||||
def test_accepts_processor_output(self):
|
||||
req = self.get_completion_request()
|
||||
processor_output, prompt = self.get_processor_output(req=req)
|
||||
output = await self.engine.async_generate(
|
||||
output = self.engine.generate(
|
||||
input_ids=processor_output["input_ids"][0].detach().cpu().tolist(),
|
||||
image_data=[self._processor_output_image_data(processor_output)],
|
||||
sampling_params=dict(temperature=0.0, max_new_tokens=512),
|
||||
@@ -207,7 +218,7 @@ class VLMInputTestBase:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class TestQwenVLUnderstandsImage(VLMInputTestBase, unittest.IsolatedAsyncioTestCase):
|
||||
class TestQwenVLUnderstandsImage(VLMInputTestBase, CustomTestCase):
|
||||
model_path = "Qwen/Qwen2.5-VL-3B-Instruct"
|
||||
chat_template = "qwen2-vl"
|
||||
|
||||
@@ -234,7 +245,7 @@ class TestQwenVLUnderstandsImage(VLMInputTestBase, unittest.IsolatedAsyncioTestC
|
||||
return dict(processor_output, format="processor_output")
|
||||
|
||||
|
||||
class TestGemmaUnderstandsImage(VLMInputTestBase, unittest.IsolatedAsyncioTestCase):
|
||||
class TestGemmaUnderstandsImage(VLMInputTestBase, CustomTestCase):
|
||||
model_path = "google/gemma-3-4b-it"
|
||||
chat_template = "gemma-it"
|
||||
|
||||
@@ -263,9 +274,7 @@ class TestGemmaUnderstandsImage(VLMInputTestBase, unittest.IsolatedAsyncioTestCa
|
||||
|
||||
|
||||
# Updated Kimi-VL test to use the new input format.
|
||||
class TestKimiVLImageUnderstandsImage(
|
||||
VLMInputTestBase, unittest.IsolatedAsyncioTestCase
|
||||
):
|
||||
class TestKimiVLImageUnderstandsImage(VLMInputTestBase, CustomTestCase):
|
||||
model_path = "moonshotai/Kimi-VL-A3B-Instruct"
|
||||
chat_template = "kimi-vl"
|
||||
|
||||
@@ -322,7 +331,7 @@ class TestKimiVLImageUnderstandsImage(
|
||||
|
||||
# not for CI: too large
|
||||
# class TestLlama4ImageUnderstandsImage(
|
||||
# VLMInputTestBase, unittest.IsolatedAsyncioTestCase
|
||||
# VLMInputTestBase, CustomTestCase
|
||||
# ):
|
||||
# # Allow overriding via env for local/offline runs.
|
||||
# model_path = "meta-llama/Llama-4-Scout-17B-16E-Instruct"
|
||||
@@ -364,7 +373,7 @@ class TestKimiVLImageUnderstandsImage(
|
||||
# return dict(processor_output, format="processor_output")
|
||||
|
||||
|
||||
# class TestLlavaUnderstandsImage(VLMInputTestBase, unittest.IsolatedAsyncioTestCase):
|
||||
# class TestLlavaUnderstandsImage(VLMInputTestBase, CustomTestCase):
|
||||
# model_path = "llava-hf/llava-1.5-7b-hf"
|
||||
# chat_template = "vicuna_v1.1"
|
||||
|
||||
@@ -403,7 +412,7 @@ class TestKimiVLImageUnderstandsImage(
|
||||
# return dict(processor_output, format="processor_output")
|
||||
|
||||
|
||||
class TestInternVLUnderstandsImage(VLMInputTestBase, unittest.IsolatedAsyncioTestCase):
|
||||
class TestInternVLUnderstandsImage(VLMInputTestBase, CustomTestCase):
|
||||
model_path = "OpenGVLab/InternVL2-2B"
|
||||
chat_template = "internvl-2-5"
|
||||
|
||||
@@ -427,6 +436,7 @@ class TestInternVLUnderstandsImage(VLMInputTestBase, unittest.IsolatedAsyncioTes
|
||||
cls.model_path, trust_remote_code=True
|
||||
)
|
||||
cls._init_visual()
|
||||
cls._start_engine()
|
||||
|
||||
@classmethod
|
||||
def _init_visual(cls):
|
||||
@@ -591,7 +601,7 @@ class TestInternVLUnderstandsImage(VLMInputTestBase, unittest.IsolatedAsyncioTes
|
||||
|
||||
|
||||
@unittest.skip("temporarily disabled: NaN in next_token_logits")
|
||||
class TestMiniCPMVUnderstandsImage(VLMInputTestBase, unittest.IsolatedAsyncioTestCase):
|
||||
class TestMiniCPMVUnderstandsImage(VLMInputTestBase, CustomTestCase):
|
||||
model_path = "openbmb/MiniCPM-V-4"
|
||||
chat_template = "minicpmv"
|
||||
|
||||
@@ -618,6 +628,7 @@ class TestMiniCPMVUnderstandsImage(VLMInputTestBase, unittest.IsolatedAsyncioTes
|
||||
cls.processor.tokenizer = get_tokenizer(cls.model_path, trust_remote_code=True)
|
||||
_fix_added_tokens_encoding(cls.processor.tokenizer)
|
||||
cls._init_visual()
|
||||
cls._start_engine()
|
||||
|
||||
@classmethod
|
||||
def _init_visual(cls):
|
||||
|
||||
Reference in New Issue
Block a user