[NPU] update nightly tests (#17952)

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: cy <chenyang08056032@163.com>
This commit is contained in:
Sugar920
2026-02-03 00:13:30 +08:00
committed by GitHub
parent c971852ffc
commit c781db0f6c
52 changed files with 1703 additions and 69 deletions

View File

@@ -0,0 +1,92 @@
import os
import unittest
import requests
from sglang.srt.utils import kill_process_tree
from sglang.test.ascend.test_ascend_utils import LLAMA_3_2_1B_INSTRUCT_WEIGHTS_PATH
from sglang.test.ci.ci_register import register_npu_ci
from sglang.test.test_utils import (
DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
DEFAULT_URL_FOR_TEST,
CustomTestCase,
popen_launch_server,
)
register_npu_ci(est_time=400, suite="nightly-1-npu-a3", nightly=True)
class TestLogLevel(CustomTestCase):
"""TestcaseVerify set log-level parameter, the printed log level is the same as the configured log level and the inference request is successfully processed.
[Test Category] Parameter
[Test Target] --log-level
"""
model = LLAMA_3_2_1B_INSTRUCT_WEIGHTS_PATH
OUT_LOG_PATH = "./out_log.txt"
ERR_LOG_PATH = "./err_log.txt"
def _launch_server_and_run_infer(self, other_args):
out_log_file = None
err_log_file = None
process = None
try:
out_log_file = open(self.OUT_LOG_PATH, "w+", encoding="utf-8")
err_log_file = open(self.ERR_LOG_PATH, "w+", encoding="utf-8")
process = popen_launch_server(
self.model,
DEFAULT_URL_FOR_TEST,
timeout=DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
other_args=other_args,
return_stdout_stderr=(out_log_file, err_log_file),
)
health_resp = requests.get(f"{DEFAULT_URL_FOR_TEST}/health_generate")
self.assertEqual(health_resp.status_code, 200)
gen_resp = requests.post(
f"{DEFAULT_URL_FOR_TEST}/generate",
json={
"text": "The capital of France is",
"sampling_params": {"temperature": 0, "max_new_tokens": 32},
},
)
self.assertEqual(gen_resp.status_code, 200)
self.assertIn("Paris", gen_resp.text)
out_log_file.seek(0)
return out_log_file.read()
finally:
kill_process_tree(process.pid)
out_log_file.close()
err_log_file.close()
os.remove(self.OUT_LOG_PATH)
os.remove(self.ERR_LOG_PATH)
def test_log_level(self):
# Verify set --log-level=warning and not set --log-level-http, logs print only warning level (no HTTP info)
other_args = [
"--log-level",
"warning",
"--attention-backend",
"ascend",
"--disable-cuda-graph",
]
log_content = self._launch_server_and_run_infer(other_args)
self.assertNotIn("POST /generate HTTP/1.1", log_content)
def test_log_http_level(self):
# Verify set --log-level=warning and set --log-level-http=info, log level print http info
other_args = [
"--log-level",
"warning",
"--log-level-http",
"info",
"--attention-backend",
"ascend",
"--disable-cuda-graph",
]
log_content = self._launch_server_and_run_infer(other_args)
self.assertIn("POST /generate HTTP/1.1", log_content)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,39 @@
import unittest
from sglang.test.ascend.test_ascend_utils import LLAMA_3_1_8B_INSTRUCT_WEIGHTS_PATH
from sglang.test.ci.ci_register import register_npu_ci
from sglang.test.test_utils import CustomTestCase, run_bench_serving, run_mmlu_test
register_npu_ci(
est_time=400,
suite="nightly-1-npu-a3",
nightly=True,
disabled="run failed",
)
class TestNoChunkedPrefill(CustomTestCase):
"""Testcase: Verify Llama-3.1-8B-Instruct accuracy ≥ 0.65 and serving normal with chunked prefill disabled.
[Test Category] Parameter
[Test Target] --chunked-prefill-size
"""
def test_no_chunked_prefill(self):
run_mmlu_test(
disable_radix_cache=False, enable_mixed_chunk=False, chunked_prefill_size=-1
)
def test_no_chunked_prefill_without_radix_cache(self):
res = run_bench_serving(
model=LLAMA_3_1_8B_INSTRUCT_WEIGHTS_PATH,
num_prompts=10,
request_rate=float("inf"),
other_server_args=["--disable-radix-cache", "--chunked-prefill-size", "-1"],
)
assert res["completed"] == 10
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,48 @@
import unittest
from sglang.test.ci.ci_register import register_npu_ci
from sglang.test.test_utils import CustomTestCase, run_mmlu_test
register_npu_ci(
est_time=400,
suite="nightly-1-npu-a3",
nightly=True,
disabled="run failed",
)
class TestOverlapSchedule(CustomTestCase):
"""Testcase: Verify that the model can successfully process inference requests and achieve an accuracy of ≥ 0.65 when the overlap scheduler is disabled,
covering all combination scenarios of radix cache (enabled/disabled) and chunked prefill (enabled/disabled).
[Test Category] Parameter
[Test Target] --disable-radix-cache;--disable-overlap
"""
def test_no_radix_attention_chunked_prefill(self):
run_mmlu_test(
disable_radix_cache=True,
chunked_prefill_size=128,
disable_overlap=True,
)
def test_no_radix_attention_no_chunked_prefill(self):
run_mmlu_test(
disable_radix_cache=True, chunked_prefill_size=-1, disable_overlap=True
)
def test_radix_attention_chunked_prefill(self):
run_mmlu_test(
disable_radix_cache=False,
chunked_prefill_size=128,
disable_overlap=True,
)
def test_radix_attention_no_chunked_prefill(self):
run_mmlu_test(
disable_radix_cache=False, chunked_prefill_size=-1, disable_overlap=True
)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,208 @@
"""Test original log probability alignment between SGLang and Hugging Face.
This test suite verifies the correctness of the `origin_logprobs` output (temperature=1)
and the `logprobs` output (temperature=0.5) in SGLang by comparing it against
raw logit-based probabilities computed directly from a reference Hugging Face model.
The test covers the following scenarios:
- Next-token prediction: Verifies that the log probability of the next token from
SGLang matches the Hugging Face model.
- Top-k logprobs: Ensures that the top-k original logprobs returned by SGLang are
consistent with Hugging Face outputs.
- Specified token IDs: Confirms that the original logprobs for specific token IDs
match the values computed from Hugging Face logits.
"""
import os
import random
import unittest
import torch
import torch.nn.functional as F
from transformers import AutoModelForCausalLM, AutoTokenizer
import sglang as sgl
from sglang.test.ascend.test_ascend_utils import LLAMA_3_2_1B_INSTRUCT_WEIGHTS_PATH
from sglang.test.ci.ci_register import register_npu_ci
# ------------------------- Configurable via env ------------------------- #
MODEL_ID = LLAMA_3_2_1B_INSTRUCT_WEIGHTS_PATH
PROMPTS = [
"Hello, my name is",
"The future of AI is",
"The president of the United States is",
"The capital of France is ",
]
TOP_LOGPROBS_NUM = 50
NUM_RANDOM_TOKEN_IDS = 10
RTOL = 0.20
ATOL = 0.00
# ------------------------------------------------
torch.manual_seed(1234)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(1234)
torch.backends.cuda.matmul.allow_tf32 = False
torch.backends.cudnn.allow_tf32 = False
register_npu_ci(est_time=400, suite="nightly-1-npu-a3", nightly=True)
class TestOriginalLogprob(unittest.TestCase):
"""Testcase: Verify the behavior and log probability alignment of SGLang under two configurations of the environment variable `SGLANG_RETURN_ORIGINAL_LOGPROB` (True/False),
by comparing SGLang's output with reference values from Hugging Face.
[Test Category] Parameter
[Test Target] SGLANG_RETURN_ORIGINAL_LOGPROB
"""
def setUp(self):
# ----- HF side (float32 weights) -----
self.tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, padding_side="right")
self.hf_model = AutoModelForCausalLM.from_pretrained(
MODEL_ID, torch_dtype=torch.float32, device_map="auto"
)
# Shared sampling parameters
self.sampling_params = {
"temperature": 0.5, # SGLang uses 0.5, but original logprobs are used 1.0
"top_p": 1.0,
"top_k": 10,
"max_new_tokens": 1,
}
# ---------------------------------------------------------------------
# Helper: compare one SGLang block (token_logprobs / top_logprobs / ids_logprobs)
# against a reference HF logprob vector.
# ---------------------------------------------------------------------
def assert_logprobs_block_equal(
self,
hf_log_probs: torch.Tensor, # [V]
token_log_probs: list,
top_log_probs: list,
ids_log_probs: list,
random_token_ids: list,
tag: str = "",
):
vals, idxs, _ = zip(*token_log_probs)
sgl_vals = torch.tensor(vals, device=self.hf_model.device, dtype=torch.float32)
sgl_idxs = torch.tensor(idxs, device=self.hf_model.device, dtype=torch.long)
hf_vals = hf_log_probs[sgl_idxs]
self.assertTrue(
torch.allclose(hf_vals, sgl_vals, rtol=RTOL, atol=ATOL),
msg=f"[{tag}] tokenlevel mismatch at indices {sgl_idxs.tolist()}",
)
hf_topk, _ = torch.topk(hf_log_probs, k=TOP_LOGPROBS_NUM, dim=-1)
sgl_topk = torch.tensor(
[float(t[0]) for t in top_log_probs[0] if t and t[0] is not None][
:TOP_LOGPROBS_NUM
],
dtype=torch.float32,
device=self.hf_model.device,
)
k = min(hf_topk.numel(), sgl_topk.numel())
self.assertTrue(
torch.allclose(hf_topk[:k], sgl_topk[:k], rtol=RTOL, atol=ATOL),
msg=f"[{tag}] topk mismatch",
)
indices = torch.tensor(
random_token_ids, dtype=torch.long, device=hf_log_probs.device
)
hf_token_ids = hf_log_probs[indices]
sgl_token_ids = torch.tensor(
[v for v, _, _ in ids_log_probs[0]],
device=self.hf_model.device,
dtype=torch.float32,
)
self.assertTrue(
torch.allclose(hf_token_ids, sgl_token_ids, rtol=RTOL, atol=ATOL),
msg=f"[{tag}] tokenIDs mismatch",
)
# Optional: print max abs diff for quick diagnostics
max_diff = torch.max(torch.abs(hf_vals - sgl_vals)).item()
print(f"[{tag}] max|diff| tokenlevel = {max_diff:.4f}")
def test_logprob_match(self):
vocab_size = self.tokenizer.vocab_size
for env_val in ["True", "False"]:
with self.subTest(return_original_logprob=env_val):
os.environ["SGLANG_RETURN_ORIGINAL_LOGPROB"] = env_val
# ----- SGLang side -----
sgl_engine = sgl.Engine(
model_path=MODEL_ID,
skip_tokenizer_init=True,
trust_remote_code=True,
mem_fraction_static=0.60,
attention_backend="ascend",
disable_cuda_graph=True,
)
for prompt in PROMPTS:
random_token_ids = sorted(
random.sample(range(vocab_size), NUM_RANDOM_TOKEN_IDS)
)
enc = self.tokenizer(prompt, return_tensors="pt")
input_ids = enc["input_ids"].to(self.hf_model.device)
attn_mask = enc["attention_mask"].to(self.hf_model.device)
with torch.inference_mode():
hf_out = self.hf_model(
input_ids=input_ids,
attention_mask=attn_mask,
return_dict=True,
)
logits = hf_out.logits[:, -1, :] # [1, V]
hf_log_probs = F.log_softmax(
logits.float() / self.sampling_params["temperature"], dim=-1
)[0]
hf_original_log_probs = F.log_softmax(logits.float(), dim=-1)[0]
outputs = sgl_engine.generate(
input_ids=input_ids[0].tolist(),
sampling_params=self.sampling_params,
return_logprob=True,
top_logprobs_num=TOP_LOGPROBS_NUM,
token_ids_logprob=random_token_ids,
)
if isinstance(outputs, list):
outputs = outputs[0]
meta = outputs["meta_info"]
# Check original logprobs only if enabled
if env_val.lower() == "true":
self.assert_logprobs_block_equal(
hf_log_probs=hf_original_log_probs,
token_log_probs=meta["output_token_logprobs"],
top_log_probs=meta["output_top_logprobs"],
ids_log_probs=meta["output_token_ids_logprobs"],
random_token_ids=random_token_ids,
tag=f"Original logprobs SGLang vs HF: {prompt} ({env_val})",
)
else:
# Always check regular logprobs
self.assert_logprobs_block_equal(
hf_log_probs=hf_log_probs,
token_log_probs=meta["output_token_logprobs"],
top_log_probs=meta["output_top_logprobs"],
ids_log_probs=meta["output_token_ids_logprobs"],
random_token_ids=random_token_ids,
tag=f"logprobs SGLang vs HF: {prompt} ({env_val})",
)
sgl_engine.shutdown()
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,92 @@
import os
import unittest
import requests
from sglang.srt.utils import kill_process_tree
from sglang.test.ascend.test_ascend_utils import MINICPM_O_2_6_WEIGHTS_PATH
from sglang.test.ci.ci_register import register_npu_ci
from sglang.test.test_utils import (
DEFAULT_URL_FOR_TEST,
CustomTestCase,
popen_launch_server,
)
register_npu_ci(
est_time=400,
suite="nightly-4-npu-a3",
nightly=True,
disabled="run failed",
)
class TestAscendWarmups(CustomTestCase):
"""Testcase: Test that the warm-up task runs successfully when the --warmups voice_chat parameter is specified upon service startup.
[Test Category] Parameter
[Test Target] --warmups
"""
model = MINICPM_O_2_6_WEIGHTS_PATH
base_url = DEFAULT_URL_FOR_TEST
@classmethod
def setUpClass(cls):
other_args = [
"--trust-remote-code",
"--warmups",
"voice_chat",
"--tp-size",
"4",
"--mem-fraction-static",
"0.8",
"--attention-backend",
"ascend",
"--disable-cuda-graph",
]
cls.out_log_file = open("./out_log.txt", "w+", encoding="utf-8")
cls.err_log_file = open("./err_log.txt", "w+", encoding="utf-8")
cls.process = popen_launch_server(
cls.model,
cls.base_url,
timeout=3600,
other_args=other_args,
return_stdout_stderr=(cls.out_log_file, cls.err_log_file),
)
@classmethod
def tearDownClass(cls):
kill_process_tree(cls.process.pid)
cls.out_log_file.close()
cls.err_log_file.close()
os.remove("./out_log.txt")
os.remove("./err_log.txt")
def test_warmups_with_voice_chat(self):
# Call the get_server_info API to verify that the warmups parameter configuration takes effect.
response = requests.get(f"{DEFAULT_URL_FOR_TEST}/get_server_info")
self.assertEqual(response.status_code, 200)
self.assertEqual("voice_chat", response.json().get("warmups"))
# Verify the actual execution of the warm-up task.
self.err_log_file.seek(0)
content = self.err_log_file.read()
self.assertIn("Running warmup voice_chat", content)
# Verify that the inference API functions properly.
response = requests.post(
f"{DEFAULT_URL_FOR_TEST}/generate",
json={
"text": "The capital of France is",
"sampling_params": {
"temperature": 0,
"max_new_tokens": 32,
},
},
)
self.assertEqual(response.status_code, 200)
self.assertIn("Paris", response.text)
if __name__ == "__main__":
unittest.main()