153 lines
4.7 KiB
Python
153 lines
4.7 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# Standard
|
|
import argparse
|
|
import contextlib
|
|
import os
|
|
import time
|
|
|
|
# Third Party
|
|
from vllm import LLM, SamplingParams
|
|
from vllm.config import KVTransferConfig
|
|
|
|
# First Party
|
|
from lmcache.integration.vllm.utils import ENGINE_NAME
|
|
from lmcache.v1.cache_engine import LMCacheEngineBuilder
|
|
|
|
|
|
def setup_environment_variables(vllm_version: str, use_disk: bool = False):
|
|
# LMCache-related environment variables
|
|
|
|
# LMCache is set to use 256 tokens per chunk
|
|
os.environ["LMCACHE_CHUNK_SIZE"] = "256"
|
|
|
|
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"
|
|
|
|
if vllm_version == "v0":
|
|
os.environ["VLLM_USE_V1"] = "0"
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def build_llm_with_lmcache(lmcache_connector: str, model: str, vllm_version: str):
|
|
ktc = KVTransferConfig(
|
|
kv_connector=lmcache_connector,
|
|
kv_role="kv_both",
|
|
)
|
|
# Set GPU memory utilization to 0.8 for an A40 GPU with 40GB
|
|
# memory. Reduce the value if your GPU has less memory.
|
|
# Note: LMCache supports chunked prefill (see vLLM#14505, LMCache#392).
|
|
#
|
|
# Pass kwargs directly to LLM() instead of routing through
|
|
# EngineArgs + asdict(). asdict() emits None for every unset EngineArgs
|
|
# field, and vLLM >= 0.20 / pydantic v2 rejects None for
|
|
# CompilationConfig fields like cudagraph_capture_sizes (list) and
|
|
# pass_config.fuse_minimax_qk_norm (bool). See issue #3438.
|
|
llm_kwargs = {
|
|
"model": model,
|
|
"kv_transfer_config": ktc,
|
|
"max_model_len": 8000,
|
|
"gpu_memory_utilization": 0.8,
|
|
}
|
|
if vllm_version == "v0":
|
|
llm_kwargs["enable_chunked_prefill"] = True # Only in v0
|
|
llm = LLM(**llm_kwargs)
|
|
try:
|
|
yield llm
|
|
finally:
|
|
# Clean up lmcache backend
|
|
LMCacheEngineBuilder.destroy(ENGINE_NAME)
|
|
|
|
|
|
def print_output(
|
|
llm: LLM,
|
|
prompt: list[str],
|
|
sampling_params: SamplingParams,
|
|
req_str: str,
|
|
):
|
|
# Should be able to see logs like the following:
|
|
# `LMCache INFO: Storing KV cache for 6006 out of 6006 tokens for request 0`
|
|
# This indicates that the KV cache has been stored in LMCache.
|
|
start = time.time()
|
|
outputs = llm.generate(prompt, 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(
|
|
"-v",
|
|
"--version",
|
|
choices=["v0", "v1"],
|
|
default="v1",
|
|
help=(
|
|
"Specify vLLM version (default: v1). "
|
|
"v0 requires vLLM <= 0.10.x; vLLM 0.11.0+ removed the V0 engine."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"-d",
|
|
"--use-disk",
|
|
action="store_true",
|
|
help="Specify whether to use disk as backend (default: False)",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
|
|
if args.version == "v0":
|
|
lmcache_connector = "LMCacheConnector"
|
|
model = "mistralai/Mistral-7B-Instruct-v0.2"
|
|
else:
|
|
lmcache_connector = "LMCacheConnectorV1"
|
|
model = "mistralai/Mistral-7B-Instruct-v0.2"
|
|
|
|
setup_environment_variables(args.version, args.use_disk)
|
|
|
|
with build_llm_with_lmcache(lmcache_connector, model, args.version) as llm:
|
|
# This example script runs two requests with a shared prefix.
|
|
# Define the shared prompt and specific prompts
|
|
shared_prompt = "Hello, how are you?" * 1000
|
|
first_prompt = [
|
|
shared_prompt + "Hello, my name is",
|
|
]
|
|
second_prompt = [
|
|
shared_prompt + "Tell me a very long story",
|
|
]
|
|
|
|
sampling_params = SamplingParams(temperature=0, top_p=0.95, max_tokens=10)
|
|
|
|
# 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")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|