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

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()