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

180 lines
5.3 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# Standard
from dataclasses import asdict
import argparse
import contextlib
import json
import os
import time
# Third Party
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(raw_block_path: str, use_uring: bool = False) -> None:
"""Set up LMCache-related environment variables for the Rust raw block backend.
Configures environment variables for LMCache including chunk size, storage
plugins, and Rust raw block backend specific settings.
Args:
raw_block_path: Path to the raw block device for storage.
use_uring: Whether to enable io_uring path
Returns:
None
"""
# LMCache-related environment variables
# LMCache is set to use 256 tokens per chunk
os.environ["LMCACHE_CHUNK_SIZE"] = "256"
# Disable local CPU backend in LMCache
os.environ["LMCACHE_LOCAL_CPU"] = "False"
# Set the maximum size of the local disk size to 5GB
os.environ["LMCACHE_MAX_LOCAL_DISK_SIZE"] = "5"
os.environ["LMCACHE_STORAGE_PLUGINS"] = "raw_block"
# Raw block specific extra config
os.environ["LMCACHE_EXTRA_CONFIG"] = json.dumps(
{
"storage_plugin.raw_block.module_path": "lmcache.v1.storage_backend.plugins.rust_raw_block_backend", # noqa: E501
"storage_plugin.raw_block.class_name": "RustRawBlockBackend",
"rust_raw_block.device_path": raw_block_path,
"rust_raw_block.use_odirect": True,
"rust_raw_block.header_bytes": 4096,
"rust_raw_block.meta_total_bytes": 4 * 1024 * 1024,
"rust_raw_block.meta_enable_periodic": False,
"rust_raw_block.use_uring": use_uring,
}
)
@contextlib.contextmanager
def build_llm_with_lmcache(lmcache_connector: str, model: str):
"""Build a vLLM LLM instance with LMCache integration.
Creates a context manager that builds a vLLM LLM instance configured with
LMCache for KV cache management. The LLM is yielded and cleaned up on exit.
Args:
lmcache_connector: The LMCache connector name to use
model: The model name.
"""
ktc = KVTransferConfig(
kv_connector=lmcache_connector,
kv_role="kv_both",
)
# Set GPU memory utilization to 0.5 for an A100 GPU with 40GB
# memory. Update it accordingly for different GPU.
llm_args = EngineArgs(
model=model,
kv_transfer_config=ktc,
max_model_len=8000,
gpu_memory_utilization=0.5,
)
llm = LLM(**asdict(llm_args))
try:
yield llm
finally:
# Clean up the LMCache backend
LMCacheEngineBuilder.destroy(ENGINE_NAME)
def print_output(
llm: LLM,
prompt: list[str],
sampling_params: SamplingParams,
req_str: str,
) -> None:
"""Generate text using the LLM and print the output with timing information.
Args:
llm: The vLLM LLM instance to use for generation.
prompt: The input prompt(s) as a list of strings.
sampling_params: Sampling parameters for generation.
req_str: A string identifier for the request.
Returns:
None
"""
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.")
def parse_args() -> argparse.Namespace:
"""Parse command-line arguments for the script.
Returns:
argparse.Namespace: Parsed arguments containing:
- disk_path: Path to the raw block device for storage.
- use_uring: Whether to enable io_uring path.
"""
parser = argparse.ArgumentParser()
parser.add_argument(
"--disk_path",
type=str,
)
parser.add_argument(
"--use_uring",
action="store_true",
help="Enable io_uring path (requires Linux kernel >= 5.1)",
)
return parser.parse_args()
def main() -> None:
"""Main entry point for the Rust backend offload example.
Sets up environment variables, builds an LLM with LMCache integration,
and runs two requests with a shared prefix to demonstrate KV cache reuse.
Returns:
None
"""
args = parse_args()
connector = "LMCacheConnectorV1"
model = "Qwen/Qwen3-8B"
setup_environment_variables(args.disk_path, args.use_uring)
with build_llm_with_lmcache(connector, model) 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()