feat(SpecEagleV2): add standalone_worker_v2 (#12625)

Co-authored-by: liugaoji.lgj <liugaoji.lgj@alibaba-inc.com>
This commit is contained in:
Gaoji Liu
2025-12-30 17:55:04 +08:00
committed by GitHub
co-authored by liugaoji.lgj
parent b6871ba7c9
commit 7518dc3532
14 changed files with 317 additions and 17 deletions
@@ -1,3 +1,4 @@
import os
import unittest
from types import SimpleNamespace
@@ -41,6 +42,25 @@ DEFAULT_SERVER_ARGS = [
0.7,
]
# Default server arguments for V2 tests
DEFAULT_SERVER_ARGS_V2 = [
"--trust-remote-code",
"--cuda-graph-max-bs",
"8",
"--speculative-algorithm",
"STANDALONE",
"--speculative-draft-model-path",
DEFAULT_DRAFT_MODEL_STANDALONE,
"--speculative-num-steps",
"4",
"--speculative-eagle-topk",
"1",
"--speculative-num-draft-tokens",
"5",
"--mem-fraction-static",
0.7,
]
class TestStandaloneSpeculativeDecodingBase(CustomTestCase):
@@ -100,6 +120,67 @@ class TestStandaloneSpeculativeDecodingBase(CustomTestCase):
self.assertGreater(avg_spec_accept_length, self.spec_decode_threshold)
class TestStandaloneV2SpeculativeDecodingBase(CustomTestCase):
model = DEFAULT_TARGET_MODEL_STANDALONE
draft_model = DEFAULT_DRAFT_MODEL_STANDALONE
base_url = DEFAULT_URL_FOR_TEST
accuracy_threshold = 0.7 # derived tests need to override this
spec_decode_threshold = 3.6 # derived spec decoding tests need to override this
@classmethod
def get_server_args(cls):
"""Return the arguments for the server launch. Override in subclasses."""
return DEFAULT_SERVER_ARGS_V2 + ["--attention-backend", "fa3"]
@classmethod
def setUpClass(cls):
# disable deep gemm precompile to make launch server faster
# please don't do this if you want to make your inference workload faster
envs.SGLANG_JIT_DEEPGEMM_PRECOMPILE.set(False)
envs.SGLANG_ENABLE_JIT_DEEPGEMM.set(False)
envs.SGLANG_ENABLE_SPEC_V2.set(True) # Enable Speculative Decoding V2
model = cls.model
cls.process = popen_launch_server(
model,
cls.base_url,
timeout=DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
other_args=cls.get_server_args(),
)
@classmethod
def tearDownClass(cls):
kill_process_tree(cls.process.pid)
if "SGLANG_ENABLE_SPEC_V2" in os.environ:
envs.SGLANG_ENABLE_SPEC_V2.set(False)
def test_gsm8k(self):
requests.get(self.base_url + "/flush_cache")
args = SimpleNamespace(
num_shots=4,
num_questions=100,
max_new_tokens=512,
parallel=128,
host="http://127.0.0.1",
port=int(self.base_url.split(":")[-1]),
data_path=GSM_DATASET_PATH,
)
metrics = run_eval_few_shot_gsm8k(args)
print(f"{metrics=}")
# Use the appropriate metric key based on the test class
metric_key = "accuracy"
self.assertGreater(metrics[metric_key], self.accuracy_threshold)
server_info = requests.get(self.base_url + "/get_server_info")
avg_spec_accept_length = server_info.json()["internal_states"][0][
"avg_spec_accept_length"
]
print(f"{avg_spec_accept_length=}")
self.assertGreater(avg_spec_accept_length, self.spec_decode_threshold)
class TestStandaloneSpeculativeDecodingTriton(TestStandaloneSpeculativeDecodingBase):
@classmethod
@@ -115,5 +196,22 @@ class TestStandaloneSpeculativeDecodingFlashinfer(
return DEFAULT_SERVER_ARGS + ["--attention-backend", "flashinfer"]
class TestStandaloneV2SpeculativeDecodingTriton(
TestStandaloneV2SpeculativeDecodingBase
):
@classmethod
def get_server_args(cls):
return DEFAULT_SERVER_ARGS_V2 + ["--attention-backend", "triton"]
class TestStandaloneV2SpeculativeDecodingFlashinfer(
TestStandaloneV2SpeculativeDecodingBase
):
@classmethod
def get_server_args(cls):
return DEFAULT_SERVER_ARGS_V2 + ["--attention-backend", "flashinfer"]
if __name__ == "__main__":
unittest.main()