204 lines
6.4 KiB
Python
204 lines
6.4 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
"""``lmcache coordinator`` — launch the LMCache mp coordinator (HTTP).
|
|
|
|
The coordinator tracks mp server instances via a registry and evicts those
|
|
whose heartbeats lapse. Configuration falls back to ``LMCACHE_MP_COORDINATOR_*``
|
|
environment variables; CLI flags override them.
|
|
"""
|
|
|
|
# Standard
|
|
import argparse
|
|
|
|
# First Party
|
|
from lmcache.cli.commands.base import BaseCommand
|
|
from lmcache.logging import init_logger
|
|
|
|
logger = init_logger(__name__)
|
|
|
|
|
|
class CoordinatorCommand(BaseCommand):
|
|
"""CLI command that launches the LMCache mp coordinator (HTTP)."""
|
|
|
|
def name(self) -> str:
|
|
"""Return the subcommand name.
|
|
|
|
Returns:
|
|
The string ``"coordinator"``.
|
|
"""
|
|
return "coordinator"
|
|
|
|
def help(self) -> str:
|
|
"""Return short help text.
|
|
|
|
Returns:
|
|
Help string shown by ``lmcache -h``.
|
|
"""
|
|
return "Launch the LMCache mp coordinator (HTTP)."
|
|
|
|
def add_arguments(self, parser: argparse.ArgumentParser) -> None:
|
|
"""Add coordinator-specific arguments to the parser.
|
|
|
|
Each flag defaults to ``None`` so that unset flags fall back to the
|
|
``LMCACHE_MP_COORDINATOR_*`` environment variables (and then the
|
|
config defaults) in :meth:`execute`.
|
|
|
|
Args:
|
|
parser: The ``ArgumentParser`` for this subcommand.
|
|
"""
|
|
parser.add_argument(
|
|
"--host",
|
|
type=str,
|
|
default=None,
|
|
help="Host the coordinator's HTTP server binds to (default: 0.0.0.0).",
|
|
)
|
|
parser.add_argument(
|
|
"--port",
|
|
type=int,
|
|
default=None,
|
|
help="Port the coordinator's HTTP server binds to (default: 9300).",
|
|
)
|
|
parser.add_argument(
|
|
"--instance-timeout",
|
|
type=float,
|
|
default=None,
|
|
help=(
|
|
"Seconds without a heartbeat after which an instance is evicted "
|
|
"(default: 30)."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--health-check-interval",
|
|
type=float,
|
|
default=None,
|
|
help=(
|
|
"Seconds between health-check sweeps; 0 disables the loop "
|
|
"(default: 10)."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--eviction-check-interval",
|
|
type=float,
|
|
default=None,
|
|
help=(
|
|
"Seconds between L2 eviction sweeps; 0 disables the loop (default: 5)."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--eviction-ratio",
|
|
type=float,
|
|
default=None,
|
|
help=(
|
|
"Fraction of tracked keys (by count) to evict per cycle, "
|
|
"0.0 to 1.0 (default: 0.2)."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--trigger-watermark",
|
|
type=float,
|
|
default=None,
|
|
help=(
|
|
"Eviction fires when usage reaches this fraction of the "
|
|
"quota, 0.0 (exclusive) to 1.0 (default: 1.0)."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--chunk-size",
|
|
type=int,
|
|
default=None,
|
|
help=(
|
|
"Tokens per KV chunk: the CacheBlend match unit and the unit used "
|
|
"to resolve pin token_ids to keys. Must equal the MP servers' "
|
|
"--chunk-size (default: 256)."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--hash-algorithm",
|
|
type=str,
|
|
default=None,
|
|
help=(
|
|
"Token hash algorithm for pin key resolution; must equal the MP "
|
|
"servers' --hash-algorithm. 'blake3' (default) is self-contained; "
|
|
"other algorithms require vLLM importable in the coordinator."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--blend-probe-stride",
|
|
type=int,
|
|
default=None,
|
|
help=(
|
|
"Positions between CacheBlend match probes; 1 probes every "
|
|
"offset for full recall (default: 1)."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--timeout-keep-alive",
|
|
type=int,
|
|
default=None,
|
|
help=(
|
|
"Seconds the HTTP server keeps idle connections open "
|
|
"before closing them (default: 10)."
|
|
),
|
|
)
|
|
|
|
def execute(self, args: argparse.Namespace) -> None:
|
|
"""Build the coordinator config and serve the app with uvicorn.
|
|
|
|
Resolves config from the environment, then overrides any field whose
|
|
corresponding CLI flag was supplied.
|
|
|
|
Args:
|
|
args: Parsed CLI arguments.
|
|
|
|
Raises:
|
|
SystemExit: When coordinator dependencies are not installed.
|
|
"""
|
|
# Standard
|
|
import dataclasses
|
|
import sys
|
|
|
|
try:
|
|
# Third Party
|
|
import uvicorn
|
|
|
|
# First Party
|
|
from lmcache.v1.mp_coordinator.app import create_app
|
|
from lmcache.v1.mp_coordinator.config import MPCoordinatorConfig
|
|
except ImportError:
|
|
print(
|
|
"The 'lmcache coordinator' command requires the full lmcache "
|
|
"installation.\nInstall with: pip install lmcache",
|
|
file=sys.stderr,
|
|
)
|
|
sys.exit(1)
|
|
|
|
config = MPCoordinatorConfig.from_env()
|
|
|
|
overrides = {
|
|
field: value
|
|
for field, value in (
|
|
("host", args.host),
|
|
("port", args.port),
|
|
("instance_timeout", args.instance_timeout),
|
|
("health_check_interval", args.health_check_interval),
|
|
("eviction_check_interval", args.eviction_check_interval),
|
|
("eviction_ratio", args.eviction_ratio),
|
|
("trigger_watermark", args.trigger_watermark),
|
|
("chunk_size", args.chunk_size),
|
|
("hash_algorithm", args.hash_algorithm),
|
|
("blend_probe_stride", args.blend_probe_stride),
|
|
("timeout_keep_alive", args.timeout_keep_alive),
|
|
)
|
|
if value is not None
|
|
}
|
|
if overrides:
|
|
config = dataclasses.replace(config, **overrides)
|
|
|
|
app = create_app(config)
|
|
uvicorn.run(
|
|
app,
|
|
host=config.host,
|
|
port=config.port,
|
|
log_level="info",
|
|
timeout_keep_alive=config.timeout_keep_alive,
|
|
)
|