Files
2026-07-13 12:24:33 +08:00

218 lines
6.1 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# Standard
from dataclasses import asdict
import argparse
import contextlib
import os
import time
# Third Party
from transformers import AutoTokenizer
from vllm import LLM, SamplingParams
from vllm.config import KVTransferConfig
from vllm.engine.arg_utils import EngineArgs
# First Party
from lmcache.integration.vllm.utils import ENGINE_NAME
from lmcache.v1.cache_engine import LMCacheEngineBuilder
def setup_environment_variables(
use_disk: bool = False,
blend_special_str: str = " # # ",
enable_sparse: bool = False,
):
# LMCache-related environment variables
# LMCache is set to use 256 tokens per chunk
os.environ["LMCACHE_CHUNK_SIZE"] = "256"
# Blending related config
os.environ["LMCACHE_ENABLE_BLENDING"] = "True"
os.environ["LMCACHE_BLEND_SPECIAL_STR"] = blend_special_str
os.environ["LMCACHE_USE_LAYERWISE"] = "True"
os.environ["LMCACHE_BLEND_CHECK_LAYERS"] = "1"
os.environ["LMCACHE_BLEND_RECOMPUTE_RATIOS"] = "0.15"
if enable_sparse:
os.environ["VLLM_ATTENTION_BACKEND"] = "FLASHINFER"
os.environ["LMCACHE_EXTRA_CONFIG"] = '{"enable_sparse": true}'
if use_disk:
# Disable local CPU backend in LMCache
os.environ["LMCACHE_LOCAL_CPU"] = "False"
# Set the maximum size of the local CPU buffer size to 5GB
os.environ["LMCACHE_MAX_LOCAL_CPU_SIZE"] = "5"
# Enable local disk backend in LMCache
os.environ["LMCACHE_LOCAL_DISK"] = "file://local_disk/"
# Set the maximum size of the local disk size to 10GB
os.environ["LMCACHE_MAX_LOCAL_DISK_SIZE"] = "10"
else:
# Enable local CPU backend in LMCache
os.environ["LMCACHE_LOCAL_CPU"] = "True"
# Set the maximum size of the local CPU size to 5GB
os.environ["LMCACHE_MAX_LOCAL_CPU_SIZE"] = "5"
@contextlib.contextmanager
def build_llm_with_lmcache(lmcache_connector: str, model: str):
ktc = KVTransferConfig(
kv_connector=lmcache_connector,
kv_role="kv_both",
)
llm_args = EngineArgs(
model=model,
kv_transfer_config=ktc,
max_model_len=32648,
gpu_memory_utilization=0.7,
enable_prefix_caching=False,
enforce_eager=True,
)
llm = LLM(**asdict(llm_args))
try:
yield llm
finally:
# Clean up lmcache backend
LMCacheEngineBuilder.destroy(ENGINE_NAME)
def print_output(
llm: LLM,
prompt: list[int],
sampling_params: SamplingParams,
req_str: str,
):
start = time.time()
outputs = llm.generate(
prompts={"prompt_token_ids": prompt}, sampling_params=sampling_params
)
print("-" * 50)
for output in outputs:
generated_text = output.outputs[0].text
print(f"Generated text: {generated_text!r}")
print(f"Generation took {time.time() - start:.2f} seconds, {req_str} request done.")
print("-" * 50)
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument(
"-d",
"--use-disk",
action="store_true",
help="Specify whether to use disk as backend (default: False)",
)
parser.add_argument(
"-b",
"--blend-special-str",
default="# #",
help="Specify the special separators to separate chunks (default: '# #')",
)
parser.add_argument(
"--model",
type=str,
default="mistralai/Mistral-7B-Instruct-v0.2",
)
parser.add_argument(
"--enable-sparse",
action="store_true",
)
return parser.parse_args()
def main():
args = parse_args()
lmcache_connector = "LMCacheConnectorV1"
model = args.model
setup_environment_variables(
args.use_disk, args.blend_special_str, args.enable_sparse
)
tokenizer = AutoTokenizer.from_pretrained(model)
with build_llm_with_lmcache(lmcache_connector, model) as llm:
# Define the shared prompt and specific prompts
warmup_prompt = tokenizer.encode("Nice to meet you" * 500)[1:]
sys_prompt = [1, 733, 16289, 28793] + tokenizer.encode(
"You are a very helpful assistant. "
"Please answer the question with instructions."
)
chunk1_prompt = tokenizer.encode("Hello, how are you?" * 500)[1:]
chunk2_prompt = tokenizer.encode("Hello, what's up?" * 500)[1:]
chunk3_prompt = tokenizer.encode("Hi, what are you up to?" * 500)[1:]
blend_special_str = tokenizer.encode(os.getenv("LMCACHE_BLEND_SPECIAL_STR"))[1:]
first_prompt = (
sys_prompt
+ blend_special_str
+ chunk1_prompt
+ blend_special_str
+ chunk2_prompt
+ blend_special_str
+ chunk3_prompt
+ blend_special_str
+ tokenizer.encode("Hello, my name is")[1:]
+ [733, 28748, 16289, 28793]
)
second_prompt = (
sys_prompt
+ blend_special_str
+ chunk2_prompt
+ blend_special_str
+ chunk1_prompt
+ blend_special_str
+ chunk3_prompt
+ blend_special_str
+ tokenizer.encode("Hello, how are you?")[1:]
+ [733, 28748, 16289, 28793]
)
third_prompt = (
sys_prompt
+ blend_special_str
+ chunk2_prompt
+ blend_special_str
+ chunk1_prompt
+ blend_special_str
+ chunk3_prompt
+ blend_special_str
+ tokenizer.encode("Hello, what's up?")[1:]
+ [733, 28748, 16289, 28793]
)
sampling_params = SamplingParams(temperature=0, top_p=0.95, max_tokens=1)
print_output(llm, warmup_prompt, sampling_params, "warmup")
# Print the first output
print_output(llm, first_prompt, sampling_params, "first")
time.sleep(1)
# print the second output
print_output(
llm, second_prompt, sampling_params, "second (warming up blend code path)"
)
time.sleep(1)
# print the third output
print_output(llm, third_prompt, sampling_params, "third")
if __name__ == "__main__":
main()