chore: import upstream snapshot with attribution
Lint / lint (push) Has been cancelled
Build Docs / Deploy Docs (push) Has been cancelled
Windows CI / Windows (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:23:58 +08:00
commit 770d92cb1f
694 changed files with 114634 additions and 0 deletions
+878
View File
@@ -0,0 +1,878 @@
"""MLC LLM benchmark dataset classes"""
import argparse
import json
import random
from datetime import datetime
from typing import ClassVar, Dict, List, Optional, Tuple # noqa: UP035
import numpy as np
import pandas as pd
from datasets import load_dataset
from transformers import AutoTokenizer
from mlc_llm.bench.request_record import GroupedRequestRecord, Metrics, RequestRecord
from mlc_llm.protocol.openai_api_protocol import (
ChatCompletionMessage,
ChatCompletionRequest,
DebugConfig,
)
class Dataset:
"""The dataset base class."""
# We set a truncation limit of 100k.
truncate_length = int(1e5)
# For some that datasets (e.g., dataset that has shared common prefix),
# we need fake warmup requests to avoid prefilling common prefixes to the engine.
require_fake_warmup: bool = False
# Whether the dataset contains timestamps already.
# If the dataset comes with timestamps, the benchmark can just replay
# the requests according to their timestamps.
timestamp_available: bool = False
def generate_request_records(
self,
input_len: Optional[int],
output_len: Optional[int],
input_len_std: float = 0.0,
output_len_std: float = 0.0,
) -> List[RequestRecord]: # noqa: UP006
"""Get the raw unprocessed request records of the dataset."""
raise NotImplementedError()
class ShareGPTDataset(Dataset):
"""The dataset class for ShareGPT dataset."""
_tokenized_dataset: List[Tuple[str, List[int], int]] # noqa: UP006
apply_chat_template: bool
def __init__(
self, dataset_path: str, tokenizer: AutoTokenizer, apply_chat_template: bool
) -> None:
self.apply_chat_template = apply_chat_template
with open(dataset_path, encoding="utf-8") as f:
raw_dataset = json.load(f)
# Filter out the conversations with less than 2 turns.
_dataset = [
(data["conversations"][0]["value"], data["conversations"][1]["value"])
for data in raw_dataset
if len(data["conversations"]) >= 2 and data["conversations"][0]["from"] == "human"
]
# Tokenize the prompts and completions.
self.tokenizer = tokenizer
prompts = [prompt for prompt, _ in _dataset]
if apply_chat_template:
assert getattr(tokenizer, "chat_template", None) is not None, (
'"--apply-chat-template" is set but the tokenizer does not have chat template.'
)
prompts = [
tokenizer.apply_chat_template(
[{"role": "user", "content": prompt}],
add_generation_prompt=True,
tokenize=False,
)
for prompt in prompts
]
prompt_token_ids = list(
tokenizer(
prompts,
truncation=True,
max_length=min(tokenizer.model_max_length, self.truncate_length),
add_special_tokens=False,
).input_ids
)
completions = [completion for _, completion in _dataset]
completion_token_ids = tokenizer(
completions,
truncation=True,
max_length=min(tokenizer.model_max_length, self.truncate_length),
add_special_tokens=False,
).input_ids
self._tokenized_dataset: List[Tuple[str, List[int], int]] = [] # noqa: UP006
for i in range(len(_dataset)):
if (
len(prompt_token_ids[i]) < 4
or len(completion_token_ids[i]) < 4
or len(prompt_token_ids[i]) + len(completion_token_ids[i])
>= min(tokenizer.model_max_length, 8192)
):
# Filter out sequences that are too short or too long
continue
self._tokenized_dataset.append(
(prompts[i], prompt_token_ids[i], len(completion_token_ids[i]))
)
def generate_request_records(
self,
input_len: Optional[int],
output_len: Optional[int],
input_len_std: float = 0.0,
output_len_std: float = 0.0,
) -> List[RequestRecord]: # noqa: UP006
if self.apply_chat_template:
assert input_len is None, (
'"--apply-chat-template" is not supported when "--input-len" is specified.'
)
request_records = []
for prompt, input_token_ids, output_length in self._tokenized_dataset:
input_length = len(input_token_ids)
# If the request does not have enough length, discard it.
if input_len is not None and input_length < input_len + 4 * input_len_std:
continue
if input_len is not None:
input_length = round(
float(np.random.normal(loc=input_len, scale=input_len_std, size=1)[0])
)
input_token_ids = input_token_ids[:input_length]
input_truncated = True
else:
input_truncated = False
if output_len is not None:
output_length = round(
float(np.random.normal(loc=output_len, scale=output_len_std, size=1)[0])
)
elif output_length <= 1:
continue
request_records.append(
RequestRecord(
chat_cmpl=ChatCompletionRequest(
messages=[
{
"role": "user",
"content": (
self.tokenizer.decode(input_token_ids)
if input_truncated
else prompt
),
}
],
model="",
max_tokens=output_length,
),
metrics=Metrics(
success=False,
start_time=0,
finish_time=0,
end_to_end_latency_s=0,
input_tokens=len(input_token_ids),
),
)
)
return request_records
class LoogleDataset(Dataset):
"""The dataset class for Loogle dataset."""
task2prompt: ClassVar[Dict[str, str]] = { # noqa: UP006
"shortdep_qa": "Please answer the question based on the long texts below. \n{input}\nQuestion: {Q}\nAnswer: ", # noqa: E501
"longdep_qa": "Please answer the question based on the long texts below. \n{input}\nQuestion: {Q}\nAnswer: ", # noqa: E501
"longdep_summarization": "Please generate a summary of the below paper. \n{input}\n Summarization: ", # noqa: E501
"shortdep_cloze": "Please fill in the clozes based on the given long texts below. Each of the placeholder '<mask-n>' in the question could be an entity of Person, Location or Organiocation. The same masks represent the same entity. Output a json format answer, for example: {{'<mask-0>': 'Bob', '<mask-1>': 'Gorrosion Magazine','<mask-2>': 'Bethel Horizon'}}\n{input}\n Question: {Q} What are the masked entities? \nAnswer:", # noqa: E501
}
require_fake_warmup: bool = True
def __init__(self, tokenizer: AutoTokenizer, testset_name: str) -> None:
raw_dataset = load_dataset("bigainlco/LooGLE", testset_name, split="test")
self.tokenizer = tokenizer
self.dataset = []
self.prompt_format = self.task2prompt[testset_name]
prompts = []
generate_lens = []
questions = []
for data in raw_dataset:
prompt = data["input"]
prompts.append(prompt)
qa_pairs = eval(data["qa_pairs"])
questions.append([j["Q"] for j in qa_pairs])
generate_lens.append(
[len(tokenizer.encode(j["A"], add_special_tokens=False)) for j in qa_pairs]
)
prompt_token_ids = tokenizer(
prompts,
truncation=True,
max_length=min(tokenizer.model_max_length, self.truncate_length),
add_special_tokens=False,
).input_ids
for prompt, prompt_token_id, question, generate_len in zip(
prompts, prompt_token_ids, questions, generate_lens
):
self.dataset.append((prompt, prompt_token_id, question, generate_len))
def generate_request_records(
self,
input_len: Optional[int],
output_len: Optional[int],
input_len_std: float = 0.0,
output_len_std: float = 0.0,
) -> List[RequestRecord]: # noqa: UP006
request_records = []
for prompt, input_token_ids, questions, generate_lens in self.dataset:
input_length = round(float(np.random.normal(loc=input_len, scale=input_len_std)))
if len(input_token_ids) > input_length:
input_token_ids = input_token_ids[:input_length]
prompt = self.tokenizer.decode(input_token_ids)
grouped_request_records = []
for question, generate_len in zip(questions, generate_lens):
json_obj = {"input": prompt, "Q": question}
full_prompt = self.prompt_format.format(**json_obj)
output_length = (
round(float(np.random.normal(loc=output_len, scale=output_len_std, size=1)[0]))
if output_len is not None
else generate_len
)
grouped_request_records.append(
RequestRecord(
chat_cmpl=ChatCompletionRequest(
messages=[
{
"role": "user",
"content": full_prompt,
}
],
model="",
max_tokens=output_length,
),
metrics=Metrics(
success=False,
start_time=0,
finish_time=0,
end_to_end_latency_s=0,
input_tokens=len(input_token_ids),
),
)
)
request_records.append(
GroupedRequestRecord(
# Create a dummy ChatCompletionRequest.
chat_cmpl=ChatCompletionRequest(messages=[]),
records=grouped_request_records,
)
)
return request_records
class LLMPerfDataset(Dataset):
"""The dataset class for LLMPerf dataset."""
def __init__(self, dataset_path: str, num_requests: int, tokenizer: AutoTokenizer) -> None:
self.tokenizer = tokenizer
self.num_requests = num_requests
with open(dataset_path, encoding="utf-8") as f:
untokenized_data = f.readlines()
# Tokenize the prompts and completions.
tokenized_data = tokenizer(
untokenized_data,
truncation=True,
max_length=min(tokenizer.model_max_length, self.truncate_length),
add_special_tokens=False,
).input_ids
tokenized_data_lengths = [len(tokens) for tokens in tokenized_data]
self.dataset: List[Tuple[str, List[int], int]] = list( # noqa: UP006
zip(untokenized_data, tokenized_data, tokenized_data_lengths)
)
def generate_request_records(
self,
input_len: Optional[int] = None,
output_len: Optional[int] = None,
input_len_std: float = 250,
output_len_std: float = 0.0,
) -> List[RequestRecord]: # noqa: UP006
if input_len is None or input_len < 40:
input_len = 550
if output_len is None:
output_len = 150
request_records = []
for _ in range(self.num_requests):
input_length = round(float(np.random.normal(loc=input_len, scale=input_len_std)))
output_length = round(float(np.random.normal(loc=output_len, scale=output_len_std)))
prompt = (
"Randomly stream lines from the following text "
f"with {output_length} output tokens. "
"Don't generate eos tokens:\n\n"
)
remaining_token_length = input_length - len(
self.tokenizer.encode(prompt, add_special_tokens=False)
)
random.shuffle(self.dataset)
while remaining_token_length > 0:
for text, tokens, token_length in self.dataset:
if remaining_token_length < token_length:
prompt += self.tokenizer.decode(tokens[:remaining_token_length])
else:
prompt += text
remaining_token_length -= token_length
if remaining_token_length < 0:
break
request_records.append(
RequestRecord(
chat_cmpl=ChatCompletionRequest(
messages=[{"role": "user", "content": prompt}],
model="",
max_tokens=output_length,
debug_config=DebugConfig(ignore_eos=True),
),
metrics=Metrics(
success=False,
start_time=0,
finish_time=0,
end_to_end_latency_s=0,
input_tokens=input_length,
),
)
)
return request_records
class JSONModeEvalDataset(Dataset):
"""The dataset class for JSON dataset."""
def __init__(self, tokenizer: AutoTokenizer) -> None:
raw_dataset = load_dataset("NousResearch/json-mode-eval")
self.tokenizer = tokenizer
self.dataset = []
for data in raw_dataset["train"]:
messages = data["prompt"]
schema = {
"type": "json_object",
"schema": data["schema"],
}
num_tokens = 0
for message in messages:
num_tokens += len(
self.tokenizer.encode(message["content"], add_special_tokens=False)
)
self.dataset.append((messages, schema, num_tokens))
def generate_request_records(
self,
input_len: Optional[int],
output_len: Optional[int],
input_len_std: float = 0.0,
output_len_std: float = 0.0,
) -> List[RequestRecord]: # noqa: UP006
request_records = []
for messages, schema, num_tokens in self.dataset:
# If the request does not have enough length, discard it.
if input_len is not None and num_tokens < input_len + 4 * input_len_std:
continue
if output_len is not None:
output_length = max(
round(np.random.normal(loc=output_len, scale=output_len_std)), 1
)
else:
output_length = None
request_records.append(
RequestRecord(
chat_cmpl=ChatCompletionRequest(
messages=[
ChatCompletionMessage(content=message["content"], role=message["role"])
for message in messages
],
model="",
max_tokens=output_length,
response_format=schema,
),
metrics=Metrics(
success=False,
start_time=0,
finish_time=0,
end_to_end_latency_s=0,
input_tokens=num_tokens,
),
)
)
return request_records
class ReActDataset(Dataset):
"""The dataset class for replaying a given ReAct trace for benchmark purpose.
It is not an actual ReAct agent implementation.
"""
_dataset: List[List[Tuple[str, int, int]]] # noqa: UP006
require_fake_warmup: bool = True
prefix: str = """Solve a question answering task with interleaving Thought, Action, Observation steps. Thought can reason about the current situation, and Action can be three types:
(1) Search[entity], which searches the exact entity on Wikipedia and returns the first paragraph if it exists. If not, it will return some similar entities to search.
(2) Lookup[keyword], which returns the next sentence containing keyword in the current passage.
(3) Finish[answer], which returns the answer and finishes the task.
Here are some examples.
Question: What is the elevation range for the area that the eastern sector of the Colorado orogeny extends into?
Thought 1: I need to search Colorado orogeny, find the area that the eastern sector of the Colorado orogeny extends into, then find the elevation range of the area.
Action 1: Search[Colorado orogeny]
Observation 1: The Colorado orogeny was an episode of mountain building (an orogeny) in Colorado and surrounding areas.
Thought 2: It does not mention the eastern sector. So I need to look up eastern sector.
Action 2: Lookup[eastern sector]
Observation 2: (Result 1 / 1) The eastern sector extends into the High Plains and is called the Central Plains orogeny.
Thought 3: The eastern sector of Colorado orogeny extends into the High Plains. So I need to search High Plains and find its elevation range.
Action 3: Search[High Plains]
Observation 3: High Plains refers to one of two distinct land regions:
Thought 4: I need to instead search High Plains (United States).
Action 4: Search[High Plains (United States)]
Observation 4: The High Plains are a subregion of the Great Plains. From east to west, the High Plains rise in elevation from around 1,800 to 7,000 ft (550 to 2,130 m).[3]
Thought 5: High Plains rise in elevation from around 1,800 to 7,000 ft, so the answer is 1,800 to 7,000 ft.
Action 5: Finish[1,800 to 7,000 ft]
Question: Musician and satirist Allie Goertz wrote a song about the "The Simpsons" character Milhouse, who Matt Groening named after who?
Thought 1: The question simplifies to "The Simpsons" character Milhouse is named after who. I only need to search Milhouse and find who it is named after.
Action 1: Search[Milhouse]
Observation 1: Milhouse Mussolini Van Houten is a recurring character in the Fox animated television series The Simpsons voiced by Pamela Hayden and created by Matt Groening.
Thought 2: The paragraph does not tell who Milhouse is named after, maybe I can look up "named after".
Action 2: Lookup[named after]
Observation 2: (Result 1 / 1) Milhouse was named after U.S. president Richard Nixon, whose middle name was Milhous.
Thought 3: Milhouse was named after U.S. president Richard Nixon, so the answer is Richard Nixon.
Action 3: Finish[Richard Nixon]
Question: Which documentary is about Finnish rock groups, Adam Clayton Powell or The Saimaa Gesture?
Thought 1: I need to search Adam Clayton Powell and The Saimaa Gesture, and find which documentary is about Finnish rock groups.
Action 1: Search[Adam Clayton Powell]
Observation 1: Could not find [Adam Clayton Powell]. Similar: ['Adam Clayton Powell III', 'Seventh Avenue (Manhattan)', 'Adam Clayton Powell Jr. State Office Building', 'Isabel Washington Powell', 'Adam Powell', 'Adam Clayton Powell (film)', 'Giancarlo Esposito'].
Thought 2: To find the documentary, I can search Adam Clayton Powell (film).
Action 2: Search[Adam Clayton Powell (film)]
Observation 2: Adam Clayton Powell is a 1989 American documentary film directed by Richard Kilberg.
The film is about the rise and fall of influential African-American politician Adam Clayton Powell Jr.[3][4] It was later aired as part of the PBS series The American Experience.
Thought 3: Adam Clayton Powell (film) is a documentary about an African-American politician, not Finnish rock groups. So the documentary about Finnish rock groups must instead be The Saimaa Gesture.
Action 3: Finish[The Saimaa Gesture]
Question: What profession does Nicholas Ray and Elia Kazan have in common?
Thought 1: I need to search Nicholas Ray and Elia Kazan, find their professions, then find the profession they have in common.
Action 1: Search[Nicholas Ray]
Observation 1: Nicholas Ray (born Raymond Nicholas Kienzle Jr., August 7, 1911 June 16, 1979) was an American film director, screenwriter, and actor best known for the 1955 film Rebel Without a Cause.
Thought 2: Professions of Nicholas Ray are director, screenwriter, and actor. I need to search Elia Kazan next and find his professions.
Action 2: Search[Elia Kazan]
Observation 2: Elia Kazan was an American film and theatre director, producer, screenwriter and actor.
Thought 3: Professions of Elia Kazan are director, producer, screenwriter, and actor. So profession Nicholas Ray and Elia Kazan have in common is director, screenwriter, and actor.
Action 3: Finish[director, screenwriter, actor]
Question: Which magazine was started first Arthur's Magazine or First for Women?
Thought 1: I need to search Arthur's Magazine and First for Women, and find which was started first.
Action 1: Search[Arthur's Magazine]
Observation 1: Arthur's Magazine (1844-1846) was an American literary periodical published in Philadelphia in the 19th century.
Thought 2: Arthur's Magazine was started in 1844. I need to search First for Women next.
Action 2: Search[First for Women]
Observation 2: First for Women is a woman's magazine published by Bauer Media Group in the USA.[1] The magazine was started in 1989.
Thought 3: First for Women was started in 1989. 1844 (Arthur's Magazine) < 1989 (First for Women), so Arthur's Magazine was started first.
Action 3: Finish[Arthur's Magazine]
Question: Were Pavel Urysohn and Leonid Levin known for the same type of work?
Thought 1: I need to search Pavel Urysohn and Leonid Levin, find their types of work, then find if they are the same.
Action 1: Search[Pavel Urysohn]
Observation 1: Pavel Samuilovich Urysohn (February 3, 1898 â August 17, 1924) was a Soviet mathematician who is best known for his contributions in dimension theory.
Thought 2: Pavel Urysohn is a mathematician. I need to search Leonid Levin next and find its type of work.
Action 2: Search[Leonid Levin]
Observation 2: Leonid Anatolievich Levin is a Soviet-American mathematician and computer scientist.
Thought 3: Leonid Levin is a mathematician and computer scientist. So Pavel Urysohn and Leonid Levin have the same type of work.
Action 3: Finish[yes]
""" # noqa: E501, RUF001
def __init__(self, dataset_path: str, tokenizer: AutoTokenizer) -> None:
raw_entries: List[Dict] = [] # noqa: UP006
with open(dataset_path) as fin:
for line in fin:
line_content = json.loads(line)
raw_entries += list({"question": k, "triplets": v} for k, v in line_content.items())
self._dataset = []
max_rounds = 0
for raw_entry in raw_entries:
processed_entry = []
question = raw_entry["question"]
triplets = raw_entry["triplets"]
seq = self.prefix + question
max_rounds = max(max_rounds, len(triplets) + 1)
output_lengths: List[int] = [] # noqa: UP006
for i, triplet in enumerate(triplets):
output_lengths.append(
len(
tokenizer(
triplet["thought"]
+ "\nAction "
+ str(i + 1)
+ ": "
+ triplet["action"]
+ "\n",
truncation=True,
max_length=min(tokenizer.model_max_length, self.truncate_length),
add_special_tokens=False,
).input_ids
)
)
for i in range(1, len(triplets) + 2):
seq += "Thought " + str(i) + ":"
input_len = len(
tokenizer(
seq,
truncation=True,
max_length=min(tokenizer.model_max_length, self.truncate_length),
add_special_tokens=False,
).input_ids
)
output_length = (
output_lengths[i - 1]
if i <= len(triplets)
else int(sum(output_lengths) / len(triplets))
)
processed_entry.append((seq, input_len, output_length))
if i != len(triplets) + 1:
seq += (
triplets[i - 1]["thought"]
+ "\nAction "
+ str(i)
+ ": "
+ triplets[i - 1]["action"]
+ "\nObservation "
+ str(i)
+ ": "
+ triplets[i - 1]["observation"]
+ "\n"
)
self._dataset.append(processed_entry)
def generate_request_records(
self,
input_len: Optional[int],
output_len: Optional[int],
input_len_std: float = 0.0,
output_len_std: float = 0.0,
) -> List[RequestRecord]: # noqa: UP006
if input_len is not None or output_len is not None:
raise ValueError("ReAct dataset does not support specifying input/output length.")
request_records = []
for processed_entries in self._dataset:
grouped_request_records = []
for prompt, input_length, output_length in processed_entries:
grouped_request_records.append(
RequestRecord(
chat_cmpl=ChatCompletionRequest(
messages=[{"role": "user", "content": prompt}],
model="",
max_tokens=output_length,
),
metrics=Metrics(
success=False,
start_time=0,
finish_time=0,
end_to_end_latency_s=0,
input_tokens=input_length,
),
)
)
request_records.append(
GroupedRequestRecord(
# Create a dummy ChatCompletionRequest.
chat_cmpl=ChatCompletionRequest(messages=[]),
records=grouped_request_records,
)
)
return request_records
class WildChatDataset(Dataset):
"""The dataset class for WildChat dataset."""
apply_chat_template: bool
def __init__(self, tokenizer: AutoTokenizer, apply_chat_template: bool) -> None:
raw_dataset = load_dataset("allenai/WildChat", split="train")
self.tokenizer = tokenizer
self.apply_chat_template = apply_chat_template
# Filter out the conversations with less than 2 turns.
_dataset = [
(entry["conversation"][0]["content"], entry["conversation"][1]["content"])
for entry in raw_dataset
if len(entry["conversation"]) >= 2
and entry["conversation"][0]["role"] == "user"
and entry["conversation"][1]["role"] == "assistant"
]
prompts = []
completions = []
for prompt, completion in _dataset:
prompts.append(prompt)
completions.append(completion)
if apply_chat_template:
assert getattr(tokenizer, "chat_template", None) is not None, (
'"--apply-chat-template" is set but the tokenizer does not have chat template.'
)
prompts = [
tokenizer.apply_chat_template(
[{"role": "user", "content": prompt}],
add_generation_prompt=True,
tokenize=False,
)
for prompt in prompts
]
prompt_token_ids = list(
tokenizer(
prompts,
truncation=True,
max_length=min(tokenizer.model_max_length, self.truncate_length),
add_special_tokens=False,
).input_ids
)
completion_token_ids = tokenizer(
completions,
truncation=True,
max_length=min(tokenizer.model_max_length, self.truncate_length),
add_special_tokens=False,
).input_ids
self._tokenized_dataset: List[Tuple[str, List[int], int]] = [] # noqa: UP006
for i in range(len(_dataset)):
if len(prompt_token_ids[i]) < 4 or len(completion_token_ids[i]) < 4:
# Filter out sequences that are too short
continue
self._tokenized_dataset.append(
(prompts[i], prompt_token_ids[i], len(completion_token_ids[i]))
)
def generate_request_records(
self,
input_len: Optional[int],
output_len: Optional[int],
input_len_std: float = 0.0,
output_len_std: float = 0.0,
) -> List[RequestRecord]: # noqa: UP006
if self.apply_chat_template:
assert input_len is None, (
'"--apply-chat-template" is not supported when "--input-len" is specified.'
)
request_records = []
for prompt, input_token_ids, output_length in self._tokenized_dataset:
input_length = len(input_token_ids)
# If the request does not have enough length, discard it.
if input_len is not None and input_length < input_len + 4 * input_len_std:
continue
if input_len is not None:
input_length = round(
float(np.random.normal(loc=input_len, scale=input_len_std, size=1)[0])
)
input_token_ids = input_token_ids[:input_length]
input_truncated = True
else:
input_truncated = False
if output_len is not None:
output_length = round(
float(np.random.normal(loc=output_len, scale=output_len_std, size=1)[0])
)
elif output_length <= 1:
continue
request_records.append(
RequestRecord(
chat_cmpl=ChatCompletionRequest(
messages=[
{
"role": "user",
"content": (
self.tokenizer.decode(input_token_ids)
if input_truncated
else prompt
),
}
],
model="",
max_tokens=output_length,
),
metrics=Metrics(
success=False,
start_time=0,
finish_time=0,
end_to_end_latency_s=0,
input_tokens=len(input_token_ids),
),
)
)
return request_records
class AzureLLMInferenceDataset(Dataset):
"""The dataset class for AzureLLMInference dataset.
Reference: https://github.com/Azure/AzurePublicDataset
"""
timestamp_available: bool = True
def __init__(self, dataset_path: str, tokenizer: AutoTokenizer) -> None:
df = pd.read_csv(dataset_path)
self.tokenizer = tokenizer
# Filter out the conversations with less than 2 turns.
self.dataset = [
(
entry["TIMESTAMP"],
min(
entry["ContextTokens"],
tokenizer.model_max_length,
self.truncate_length,
),
min(
entry["GeneratedTokens"],
tokenizer.model_max_length,
self.truncate_length,
),
)
for _, entry in df.iterrows()
if entry["ContextTokens"] >= 4 and entry["GeneratedTokens"] >= 4
]
def generate_request_records(
self,
input_len: Optional[int],
output_len: Optional[int],
input_len_std: float = 0.0,
output_len_std: float = 0.0,
) -> List[RequestRecord]: # noqa: UP006
time_fmt = "%Y-%m-%d %H:%M:%S.%f"
start_time = datetime.strptime(self.dataset[0][0][:-1], time_fmt)
request_records = []
for timestamp, input_length, output_length in self.dataset:
# If the request does not have enough length, discard it.
if input_len is not None and input_length < input_len + 4 * input_len_std:
continue
if input_len is not None:
input_length = round(
float(np.random.normal(loc=input_len, scale=input_len_std, size=1)[0])
)
if output_len is not None:
output_length = round(
float(np.random.normal(loc=output_len, scale=output_len_std, size=1)[0])
)
elif output_length <= 1:
continue
prompt_token_ids = [
random.randint(0, self.tokenizer.vocab_size - 1) for _ in range(input_length)
]
while True:
# Adjust the token ids until the retokenization on the decoded string
# matches the required input length.
prompt = self.tokenizer.decode(prompt_token_ids)
retokenized_token_ids = self.tokenizer.encode(prompt, add_special_tokens=False)
if len(retokenized_token_ids) < input_length:
prompt_token_ids = retokenized_token_ids + [
random.randint(0, self.tokenizer.vocab_size - 1)
for _ in range(input_length - len(retokenized_token_ids))
]
elif len(retokenized_token_ids) > input_length:
prompt_token_ids = retokenized_token_ids[:input_length]
else:
break
time_diff = (datetime.strptime(timestamp[:-1], time_fmt) - start_time).total_seconds()
request_records.append(
RequestRecord(
chat_cmpl=ChatCompletionRequest(
messages=[{"role": "user", "content": prompt}],
model="",
max_tokens=output_length,
),
timestamp=time_diff,
metrics=Metrics(
success=False,
start_time=0,
finish_time=0,
end_to_end_latency_s=0,
input_tokens=input_length,
),
)
)
return request_records
SUPPORTED_DATASET = [
"sharegpt",
"llmperf",
"json-mode-eval",
"loogle",
"react",
"wildchat",
"azure-llm-inference",
]
def create_dataset(args: argparse.Namespace, tokenizer: AutoTokenizer) -> Dataset:
"""Create a dataset instance with regard to the specified dataset kind and file path."""
if args.dataset_path is not None and not isinstance(args.dataset_path, str):
raise TypeError(f"Invalid dataset path {args.dataset_path}. Please use a string.")
if args.dataset is None and args.dataset_path is not None:
# Auto-detect the dataset kind by looking into the dataset path.
if "sharegpt" in args.dataset_path.lower():
args.dataset = "sharegpt"
else:
raise ValueError(
f"Unable to detect the dataset kind from dataset path {args.dataset_path}. "
'Please specify the dataset kind via "--dataset".'
)
if args.dataset == "sharegpt":
if args.dataset_path is None:
raise ValueError(
'ShareGPT dataset requires dataset path. Please specify it with "--dataset-path".'
)
return ShareGPTDataset(args.dataset_path, tokenizer, args.apply_chat_template)
if args.dataset == "llmperf":
if args.dataset_path is None:
raise ValueError(
'LLMPerf dataset requires dataset path. Please specify it with "--dataset-path".'
)
assert args.apply_chat_template is False, (
"LLMPerf dataset does not support applying chat template"
)
return LLMPerfDataset(
args.dataset_path,
(args.num_requests + args.num_warmup_requests) * 4,
tokenizer,
)
if args.dataset == "json-mode-eval":
assert args.apply_chat_template is False, (
"JSON mode evaluation does not support applying chat template"
)
return JSONModeEvalDataset(tokenizer)
if args.dataset == "loogle":
if args.dataset_path is None:
raise ValueError(
'Loogle dataset requires a testset name. Please specify it with "--dataset-path".'
)
assert args.apply_chat_template is False, (
"Loogle dataset does not support applying chat template"
)
return LoogleDataset(tokenizer, testset_name=args.dataset_path)
if args.dataset == "react":
if args.dataset_path is None:
raise ValueError(
'ReAct dataset requires dataset path. Please specify it with "--dataset-path".'
)
assert args.apply_chat_template is False, (
"ReAct dataset does not support applying chat template"
)
return ReActDataset(args.dataset_path, tokenizer)
if args.dataset == "wildchat":
return WildChatDataset(tokenizer, args.apply_chat_template)
if args.dataset == "azure-llm-inference":
if args.dataset_path is None:
raise ValueError(
"AzureLLMInference dataset requires dataset path. "
'Please specify it with "--dataset-path".'
)
assert args.apply_chat_template is False, (
"AzureLLMInference dataset does not support applying chat template"
)
return AzureLLMInferenceDataset(args.dataset_path, tokenizer)
raise ValueError(f"Unrecognized dataset {args.dataset}")