218 lines
6.1 KiB
Python
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()
|