Files
vllm-omni/vllm_omni/diffusion/diffusion_kv/kv_connector.py
T

183 lines
6.8 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
"""Diffusion-side assembly facade built on the upstream Mooncake KV connector.
The configured ``kv_connector`` (currently ``MooncakeConnector``) is created
through vLLM's native ``KVConnectorFactory``. PR0 only validates configuration
and creates the Scheduler/Worker connector objects; it does not subclass or
reimplement Mooncake's transfer runtime. Real page sizing, cache registration,
admission, and remote loading belong to the follow-up landing PR.
"""
from __future__ import annotations
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any
import torch
from vllm.config import KVTransferConfig, VllmConfig
from vllm.distributed.kv_transfer.kv_connector.factory import KVConnectorFactory
from vllm.distributed.kv_transfer.kv_connector.v1.base import KVConnectorRole
from vllm.distributed.kv_transfer.kv_transfer_state import ensure_kv_transfer_initialized, ensure_kv_transfer_shutdown
from vllm.logger import init_logger
from vllm.v1.kv_cache_interface import KVCacheConfig
if TYPE_CHECKING:
from vllm.distributed.kv_transfer.kv_connector.v1.base import KVConnectorBase_V1
from vllm_omni.diffusion.data import OmniDiffusionConfig
logger = init_logger(__name__)
def mint_transfer_id(request_id: str) -> str:
"""Return the stable ticket shared by one AR -> Diffusion handoff."""
return f"xfer-{request_id}"
def build_source_kv_transfer_params(
*,
transfer_id: str,
remote_engine_id: str | None,
remote_bootstrap_addr: str | None,
) -> dict[str, Any]:
"""Build the opaque producer-side metadata bag for an AR request."""
params: dict[str, Any] = {
"transfer_id": transfer_id,
"do_remote_decode": True,
"do_remote_prefill": False,
}
if remote_engine_id:
params["remote_engine_id"] = remote_engine_id
if remote_bootstrap_addr:
params["remote_bootstrap_addr"] = remote_bootstrap_addr
return params
def build_target_kv_transfer_params(
*,
source_params: Mapping[str, Any],
remote_engine_id: str | None,
remote_bootstrap_addr: str | None,
) -> dict[str, Any]:
"""Build the consumer-side metadata bag without interpreting pages."""
params = dict(source_params)
params["do_remote_prefill"] = True
params["do_remote_decode"] = False
if remote_engine_id:
params["remote_engine_id"] = remote_engine_id
if remote_bootstrap_addr:
params["remote_bootstrap_addr"] = remote_bootstrap_addr
return params
def bootstrap_addr_from_kv_transfer_config(kv_transfer_config: KVTransferConfig | None) -> str | None:
"""Read an optional connector bootstrap endpoint from native config."""
if kv_transfer_config is None:
return None
extra_config = kv_transfer_config.kv_connector_extra_config or {}
for key in ("bootstrap_addr", "prefill_bootstrap_addr", "mooncake_master"):
value = extra_config.get(key)
if isinstance(value, str) and value.strip():
return value
return None
def parse_kv_transfer_config(value: object | None) -> KVTransferConfig | None:
"""Normalize a YAML mapping or an existing native config."""
if value is None:
return None
if isinstance(value, KVTransferConfig):
_validate_kv_transfer_config(value)
return value
if isinstance(value, Mapping):
payload = dict(value)
if not payload:
return None
engine_id = payload.get("engine_id")
if not isinstance(engine_id, str) or not engine_id.strip():
raise ValueError("Diffusion native kv_transfer_config requires a non-empty engine_id")
config = KVTransferConfig(**payload)
_validate_kv_transfer_config(config)
return config
raise TypeError(f"kv_transfer_config must be a mapping or KVTransferConfig, got {type(value)!r}")
def create_scheduler_kv_connector(
od_config: OmniDiffusionConfig,
) -> KVConnectorBase_V1 | None:
"""Create a Scheduler-role connector when native config is present."""
kv_transfer_config = getattr(od_config, "kv_transfer_config", None)
if kv_transfer_config is None:
return None
if not isinstance(kv_transfer_config, KVTransferConfig):
raise TypeError(
f"Diffusion native kv_transfer_config must be KVTransferConfig, got {type(kv_transfer_config)!r}"
)
vllm_config = _build_native_vllm_config(od_config)
connector = KVConnectorFactory.create_connector(
config=vllm_config,
role=KVConnectorRole.SCHEDULER,
kv_cache_config=_empty_kv_cache_config(),
)
logger.info(
"Created KV connector stub (SCHEDULER role): connector=%s engine_id=%s",
kv_transfer_config.kv_connector,
kv_transfer_config.engine_id,
)
return connector
def init_worker_kv_connector(vllm_config: VllmConfig, kv_cache_config: KVCacheConfig) -> None:
"""Initialize the Worker-role connector with its rank-local cache plan."""
if vllm_config.kv_transfer_config is None:
return
ensure_kv_transfer_initialized(vllm_config, kv_cache_config)
logger.info(
"Initialized KV connector stub (WORKER role): connector=%s engine_id=%s",
vllm_config.kv_transfer_config.kv_connector,
vllm_config.kv_transfer_config.engine_id,
)
def shutdown_kv_connector(*, scheduler_connector: KVConnectorBase_V1 | None = None) -> None:
"""Shutdown Worker and Scheduler connector objects idempotently."""
if scheduler_connector is not None:
scheduler_connector.shutdown()
ensure_kv_transfer_shutdown()
def _empty_kv_cache_config() -> KVCacheConfig:
"""Empty cache config used only for PR0 connector assembly."""
return KVCacheConfig(num_blocks=0, kv_cache_tensors=[], kv_cache_groups=[])
def _build_native_vllm_config(od_config: OmniDiffusionConfig) -> VllmConfig:
from vllm_omni.diffusion.vllm_config import create_diffusion_vllm_config
kv_transfer_config = getattr(od_config, "kv_transfer_config", None)
assert isinstance(kv_transfer_config, KVTransferConfig)
vllm_config = create_diffusion_vllm_config(torch.device("cpu"), od_config)
vllm_config.kv_transfer_config = kv_transfer_config
return vllm_config
def _validate_kv_transfer_config(config: KVTransferConfig) -> None:
engine_id = config.engine_id
if not isinstance(engine_id, str) or not engine_id.strip():
raise ValueError("Diffusion native kv_transfer_config requires a non-empty engine_id")
if config.kv_connector is not None and config.kv_role is None:
raise ValueError("Diffusion native kv_transfer_config requires kv_role when kv_connector is set")
if config.kv_role not in (None, "kv_consumer", "kv_producer", "kv_both"):
raise ValueError(f"Unsupported KV connector role: {config.kv_role!r}")