Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

522 lines
18 KiB
Python

import json
import os
import random
import string
import numpy as np
from PIL import Image
from transformers import AutoTokenizer
def load_jsonl(path):
"""Load data from a JSONL file, one JSON object per line."""
data = []
with open(path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if line:
data.append(json.loads(line))
return data
def save_jsonl(data, file_path):
"""Save a list of dicts to a JSONL file, one JSON object per line."""
file_dir = os.path.dirname(file_path)
if file_dir:
os.makedirs(file_dir, exist_ok=True)
with open(file_path, "w", encoding="utf-8") as f:
for item in data:
f.write(json.dumps(item, ensure_ascii=False) + "\n")
def format_qa(item):
"""Format a GSM8K data entry into QA text for the few-shot pool."""
question = item["question"]
answer = item["answer"]
return f"Question: {question}\nLet's think step by step\nAnswer:\n{answer}\n\n"
def pad_to_target_tokens(
question,
few_shot_pool_token_ids,
tokenizer,
target_tokens,
test_template="Question: {question}\nLet's think step by step\nAnswer:\n",
):
"""Pad a question text to the target token length.
Tokenizes the question using the test_template, calculates the remaining tokens
needed, and prepends randomly sampled few-shot token ids from the pool to reach
target_tokens. If the few-shot pool is insufficient, repeats the first sample
to fill the remaining gap.
Args:
question: The test question text.
few_shot_pool_token_ids: List of token id lists from the few-shot training pool.
tokenizer: The tokenizer instance.
target_tokens: Target input token length.
test_template: Question template string, defaults to GSM8K format.
"""
test_prompt = test_template.format(question=question)
test_token_ids = tokenizer.encode(test_prompt, add_special_tokens=False)
remaining_tokens = target_tokens - len(test_token_ids)
if remaining_tokens <= 0:
return tokenizer.decode(
test_token_ids[:target_tokens], skip_special_tokens=True
)
shuffled_ids = list(range(len(few_shot_pool_token_ids)))
random.shuffle(shuffled_ids)
prefix_ids = []
for idx in shuffled_ids:
fs_ids = few_shot_pool_token_ids[idx]
if len(prefix_ids) + len(fs_ids) <= remaining_tokens:
prefix_ids.extend(fs_ids)
else:
partial_gap = remaining_tokens - len(prefix_ids)
if partial_gap > 0:
prefix_ids.extend(fs_ids[:partial_gap])
break
if len(prefix_ids) < remaining_tokens and few_shot_pool_token_ids:
padding_source_ids = few_shot_pool_token_ids[shuffled_ids[0]]
repeat_count = (remaining_tokens // len(padding_source_ids)) + 1
padding_ids = (padding_source_ids * repeat_count)[
: remaining_tokens - len(prefix_ids)
]
prefix_ids.extend(padding_ids)
full_ids = prefix_ids + test_token_ids
return tokenizer.decode(full_ids[:target_tokens], skip_special_tokens=True)
def generate_custom_dataset(
train_path,
test_path,
tokenizer_path,
target_tokens,
num_prompts,
trust_remote_code=False,
test_template="Question: {question}\nLet's think step by step\nAnswer:\n",
):
"""Generate a custom dataset with a fixed input token length.
Builds a few-shot pool from the training set and pads test questions to the
specified token length. If the test set has fewer samples than num_prompts,
it cycles and repeats to fill the required count.
Args:
train_path: Path to the GSM8K training JSONL file.
test_path: Path to the GSM8K test JSONL file.
tokenizer_path: Path to the tokenizer.
target_tokens: Target input token length.
num_prompts: Number of prompts to generate; 0 means use all test samples.
trust_remote_code: Whether to trust remote code when loading the tokenizer.
test_template: Question template string.
Returns:
list[dict]: Each item contains fields defined in test_template.
"""
tokenizer = AutoTokenizer.from_pretrained(
tokenizer_path, trust_remote_code=trust_remote_code
)
train_data = load_jsonl(train_path)
test_data = load_jsonl(test_path)
if num_prompts > 0 and num_prompts > len(test_data):
multiplier = (num_prompts // len(test_data)) + 1
test_data = (test_data * multiplier)[:num_prompts]
elif num_prompts > 0:
test_data = test_data[:num_prompts]
few_shot_pool = [format_qa(item) for item in train_data]
few_shot_pool_token_ids = [
tokenizer.encode(fs, add_special_tokens=False) for fs in few_shot_pool
]
output_data = []
for i, test_item in enumerate(test_data):
padded_question = pad_to_target_tokens(
question=test_item["question"],
few_shot_pool_token_ids=few_shot_pool_token_ids,
tokenizer=tokenizer,
target_tokens=target_tokens,
test_template=test_template,
)
output_data.append(
{
"question": padded_question,
"answer": test_item["answer"],
}
)
if (i + 1) % 100 == 0:
actual_tokens = len(
tokenizer.encode(padded_question, add_special_tokens=False)
)
print(
f"Processed {i + 1}/{len(test_data)}, last item tokens: {actual_tokens}"
)
token_counts = [
len(tokenizer.encode(item["question"], add_special_tokens=False))
for item in output_data
]
print(
f"Token count stats: min={min(token_counts)}, max={max(token_counts)}, avg={sum(token_counts)/len(token_counts):.1f}"
)
return output_data
def generate_random_images(mm_dataset_data, size):
"""Generate random image files for a multimodal dataset.
Creates random RGB images at the specified resolution for each image path
listed in the dataset entries.
Args:
mm_dataset_data: List of multimodal data entries, each with a "path" field
containing a list of image file paths.
size: Image size tuple (width, height), e.g. (1080, 1920).
"""
total_image_num = len(mm_dataset_data)
print(f"begin to generate images, total {total_image_num}")
file_count = 0
for item in mm_dataset_data:
image_paths = item.get("path")
for image_path in image_paths:
if not image_path:
print("Error: The image path is none.")
continue
dir_name = os.path.dirname(image_path)
if dir_name and not os.path.exists(dir_name):
os.makedirs(dir_name, exist_ok=True)
random_array = np.random.randint(
0, 256, (size[1], size[0], 3), dtype=np.uint8
)
img = Image.fromarray(random_array)
img.save(image_path, quality=95)
if os.path.isfile(image_path):
file_count += 1
print(f"Finish images generation. Image num: {file_count}")
def generate_mm_dataset(
train_path,
test_path,
tokenizer_path,
target_tokens=3500,
num_prompts=1024,
trust_remote_code=False,
test_template="Question: {question}\nLet's think step by step\nAnswer:\n",
image_dir="/tmp/datasets/image",
size=None,
):
"""Generate a multimodal (text + image) dataset.
First generates fixed-length text data via generate_fixed_len_dataset, then
attaches random image paths and type labels to each entry, and generates
the corresponding random image files.
Args:
train_path: Path to the GSM8K training JSONL file.
test_path: Path to the GSM8K test JSONL file.
tokenizer_path: Path to the tokenizer.
target_tokens: Target input token length.
num_prompts: Number of prompts to generate.
trust_remote_code: Whether to trust remote code when loading the tokenizer.
test_template: Question template string.
image_dir: Directory to save generated image files.
size: Image size string in "widthxheight" format, e.g. "1080x1920".
Returns:
list[dict]: Each item contains "question", "answer", "type", and "path" fields.
"""
output_data = []
text_data = generate_custom_dataset(
train_path,
test_path,
tokenizer_path,
target_tokens,
num_prompts,
trust_remote_code,
test_template,
)
for item in text_data:
random_string = "".join(
random.choices(string.ascii_letters + string.digits, k=10)
)
item["type"] = "image"
item["path"] = [f"{image_dir}/{random_string}.jpg"]
output_data.append(item)
size = tuple(map(int, size.split("x")))
generate_random_images(output_data, size)
return output_data
def generate_gsm8k_dataset(
model_path, source_dataset_path, batch_size, input_len, output_file
):
"""Generate a dataset with a fixed input token length from GSM8K (JSONL format).
Reads GSM8K source data, repeats or truncates each question's tokens to input_len,
then trims or replicates the dataset to batch_size entries, shuffles, and writes
to the output file.
Args:
model_path: Model path used to load the tokenizer.
source_dataset_path: Path to the GSM8K source JSONL file.
batch_size: Number of samples to generate.
input_len: Target input token length.
output_file: Output JSONL file path.
"""
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
dataset = []
with open(source_dataset_path, "r", encoding="utf-8") as f:
for line in f:
data = json.loads(line)
dataset.append(data["question"])
dataset_new = []
for sentence in dataset:
words = tokenizer.tokenize(sentence)
len_num = len(words) // input_len
if len_num == 0:
multiplier = (input_len // len(words)) + 1
repeated_len = words * multiplier
words = repeated_len[:input_len]
decoded_text = tokenizer.convert_tokens_to_string(words)
if len(words) != input_len:
print(
f"Generate DataSet Error: the length of new input is {len(words)}, not {input_len}"
)
dataset_new.append(decoded_text)
batch_num = len(dataset_new) // batch_size
if batch_num == 0:
multiplier = (batch_size // len(dataset_new)) + 1
repeated_batch = dataset_new * multiplier
dataset_new = repeated_batch[:batch_size]
else:
dataset_new = dataset_new[:batch_size]
random.shuffle(dataset_new)
if len(dataset_new) != batch_size:
print(
f"Generate DataSet Error: the size of new dataset is {len(dataset_new)}, not {batch_size}"
)
output_dir = os.path.dirname(output_file)
if output_dir:
os.makedirs(output_dir, exist_ok=True)
with open(output_file, "w", encoding="utf-8") as f:
for i in range(len(dataset_new)):
f.write(
json.dumps(
{"question": f"{dataset_new[i]}", "answer": "none"},
ensure_ascii=False,
)
)
f.write("\n")
def generate_random_dataset(
model_path,
source_dataset_path,
batch_size,
input_len,
output_file,
output_len=1024,
range_ratio=1,
):
"""Generate a random dataset with logic matching bench_serving's --dataset-name random.
Samples real conversation text from the ShareGPT dataset as prompts, adjusting
to the target token length via truncation or repetition. Input/output lengths
are randomly sampled from [target*range_ratio, target]. Output format is a
JSON array compatible with ais_bench's ShareGPTDataset.
If source_dataset_path is not a valid JSON file, automatically downloads the
ShareGPT dataset from HuggingFace (anon8231489123/ShareGPT_Vicuna_unfiltered).
Args:
model_path: Model path used to load the tokenizer.
source_dataset_path: Path to the ShareGPT JSON file; auto-downloaded if invalid.
batch_size: Number of samples to generate.
input_len: Target input token length.
output_file: Output JSON file path.
output_len: Target output token length, default 1024.
range_ratio: Random range ratio for input/output lengths. Actual lengths are
uniformly sampled from [target*range_ratio, target]. Default 1 (fixed length).
"""
SHAREGPT_REPO_ID = "anon8231489123/ShareGPT_Vicuna_unfiltered"
SHAREGPT_FILENAME = "ShareGPT_V3_unfiltered_cleaned_split.json"
def _is_file_valid_json(path):
"""Check if the path points to a valid JSON file (exists and parseable)."""
if not os.path.isfile(path):
return False
try:
with open(path, encoding="utf-8") as f:
json.load(f)
return True
except json.JSONDecodeError:
return False
def _download_and_cache_hf_file(repo_id, filename, repo_type="dataset"):
"""Download and cache a file from HuggingFace Hub."""
from huggingface_hub import hf_hub_download
return hf_hub_download(repo_id=repo_id, filename=filename, repo_type=repo_type)
tokenizer = AutoTokenizer.from_pretrained(model_path)
# Randomly sample input/output lengths per request in [target*range_ratio, target]
input_lens = np.random.randint(
max(int(input_len * range_ratio), 1),
input_len + 1,
size=batch_size,
).tolist()
output_lens = np.random.randint(
max(int(output_len * range_ratio), 1),
output_len + 1,
size=batch_size,
).tolist()
# Subtract special tokens to ensure the actual encoded length does not exceed target
num_special_tokens = int(tokenizer.num_special_tokens_to_add())
for i in range(batch_size):
input_lens[i] = max(1, input_lens[i] - num_special_tokens)
# Auto-download ShareGPT dataset from HuggingFace if local file is invalid
if not _is_file_valid_json(source_dataset_path):
print(
f"source_dataset_path '{source_dataset_path}' is not a valid file, downloading from HuggingFace..."
)
source_dataset_path = _download_and_cache_hf_file(
repo_id=SHAREGPT_REPO_ID,
filename=SHAREGPT_FILENAME,
)
# Load ShareGPT dataset, filter for >=2 turns, take the first turn (human) as prompt
with open(source_dataset_path, "r", encoding="utf-8") as f:
dataset = json.load(f)
dataset = [
data
for data in dataset
if len(data.get("conversations", data.get("conversation", []))) >= 2
]
dataset = [
(
data.get("conversations", data.get("conversation", []))[0]["value"],
data.get("conversations", data.get("conversation", []))[1]["value"],
)
for data in dataset
]
random.shuffle(dataset)
# Sample prompts, truncating or repeating tokens to reach target input length
input_requests = []
for data in dataset:
i = len(input_requests)
if i == batch_size:
break
prompt = data[0]
prompt_token_ids = tokenizer.encode(prompt)
prompt_len = len(prompt_token_ids)
if prompt_len == 0:
continue
if prompt_len > input_lens[i]:
input_ids = prompt_token_ids[: input_lens[i]]
else:
ratio = (input_lens[i] + prompt_len - 1) // prompt_len
input_ids = (prompt_token_ids * ratio)[: input_lens[i]]
input_content = tokenizer.decode(input_ids)
# Output format compatible with ais_bench ShareGPTDataset
input_requests.append(
{
"id": str(i),
"conversations": [
{"from": "human", "value": input_content},
{"from": "gpt", "value": "none"},
],
}
)
print(f"#Input tokens: {np.sum(input_lens[:len(input_requests)])}")
print(f"#Output tokens: {np.sum(output_lens[:len(input_requests)])}")
output_dir = os.path.dirname(output_file)
if output_dir:
os.makedirs(output_dir, exist_ok=True)
# Output as JSON array format, compatible with ais_bench's json.load()
with open(output_file, "w", encoding="utf-8") as f:
json.dump(input_requests, f, ensure_ascii=False, indent=2)
def main():
import argparse
parser = argparse.ArgumentParser(
description="Generate GSM8K dataset with exact input token length"
)
parser.add_argument(
"--train_path", type=str, required=True, help="Path to GSM8K train.jsonl"
)
parser.add_argument(
"--test_path", type=str, required=True, help="Path to GSM8K test.jsonl"
)
parser.add_argument(
"--output_path", type=str, required=True, help="Output jsonl path"
)
parser.add_argument(
"--tokenizer_path", type=str, required=True, help="Path to model tokenizer"
)
parser.add_argument(
"--target_tokens", type=int, default=3500, help="Target input token length"
)
parser.add_argument(
"--trust_remote_code",
action="store_true",
help="Trust remote code for tokenizer",
)
parser.add_argument(
"--num_prompts",
type=int,
default=0,
help="Number of prompts to generate, 0 means all",
)
args = parser.parse_args()
output_data = generate_custom_dataset(
train_path=args.train_path,
test_path=args.test_path,
tokenizer_path=args.tokenizer_path,
target_tokens=args.target_tokens,
num_prompts=args.num_prompts,
trust_remote_code=args.trust_remote_code,
)
save_jsonl(output_data, args.output_path)
print(f"Done! Output {len(output_data)} items to {args.output_path}")
if __name__ == "__main__":
main()