Signed-off-by: tzhouam <tzhouam@connect.ust.hk> Signed-off-by: Zhou Taichang <tzhouam@connect.ust.hk> Signed-off-by: Yuanheng Zhao <jonathan.zhaoyh@gmail.com> Co-authored-by: Yuanheng Zhao <54058983+yuanheng-zhao@users.noreply.github.com>
194 lines
7.4 KiB
Python
194 lines
7.4 KiB
Python
"""Monkey-patch vLLM's MooncakeConnector to fix request-ID mismatch in PD disaggregation.
|
|
|
|
vLLM's InputProcessor appends a random suffix to each request ID. The prefill
|
|
engine stores KV under its suffix, but the decode engine generates a different
|
|
suffix. This patch threads ``remote_request_id`` through ``kv_transfer_params``
|
|
so the decode side references the correct KV entry.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import logging
|
|
import sys
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_patched: bool = False
|
|
|
|
|
|
@dataclass
|
|
class PatchedRecvReqMeta:
|
|
"""Receive-request metadata carrying the prefill engine's request ID."""
|
|
|
|
request_id: str
|
|
remote_request_id: str
|
|
local_block_ids: list[int]
|
|
kv_transfer_params: dict[str, Any]
|
|
|
|
|
|
def _import_mooncake_module():
|
|
"""Import MooncakeConnector module, supporting both vLLM >=0.16 and older."""
|
|
for mod_path in (
|
|
"vllm.distributed.kv_transfer.kv_connector.v1.mooncake.mooncake_connector",
|
|
"vllm.distributed.kv_transfer.kv_connector.v1.mooncake_connector",
|
|
):
|
|
try:
|
|
return importlib.import_module(mod_path)
|
|
except (ImportError, ModuleNotFoundError):
|
|
continue
|
|
return None
|
|
|
|
|
|
def _create_patched_mooncake_connector():
|
|
"""Return a subclass of MooncakeConnector with remote_request_id support."""
|
|
_mc_mod = _import_mooncake_module()
|
|
if _mc_mod is None:
|
|
raise ImportError("Cannot import MooncakeConnector from upstream vLLM")
|
|
_OriginalMooncakeConnector = _mc_mod.MooncakeConnector
|
|
|
|
class PatchedMooncakeConnector(_OriginalMooncakeConnector):
|
|
"""Fixes request-ID mismatch in PD disaggregation by injecting
|
|
remote_request_id on the prefill side and using it for KV lookup
|
|
on the decode side.
|
|
"""
|
|
|
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
super().__init__(*args, **kwargs)
|
|
self.remote_to_local_req: dict[str, str] = {}
|
|
logger.info("[PatchedMooncakeConnector] Initialized")
|
|
|
|
def request_finished(
|
|
self,
|
|
request: Any,
|
|
block_ids: list[int],
|
|
) -> tuple[bool, dict[str, Any] | None]:
|
|
result = super().request_finished(request, block_ids)
|
|
|
|
if isinstance(result, tuple) and len(result) == 2:
|
|
delay_free, kv_params = result
|
|
else:
|
|
delay_free, kv_params = False, result
|
|
|
|
# Normalise _reqs_need_send values
|
|
req_id = getattr(request, "request_id", None)
|
|
if req_id and hasattr(self, "_reqs_need_send"):
|
|
entry = self._reqs_need_send.get(req_id)
|
|
if isinstance(entry, tuple) and len(entry) == 2:
|
|
self._reqs_need_send[req_id] = entry[1]
|
|
|
|
# Inject remote_request_id into kv_transfer_params
|
|
if kv_params is not None and isinstance(kv_params, dict):
|
|
kv_params["remote_request_id"] = req_id or "NOT_SET"
|
|
if hasattr(self, "side_channel_host"):
|
|
kv_params.setdefault("remote_host", self.side_channel_host)
|
|
if hasattr(self, "side_channel_port"):
|
|
kv_params.setdefault("remote_port", self.side_channel_port)
|
|
|
|
return delay_free, kv_params
|
|
|
|
def add_new_req(
|
|
self,
|
|
request_id: str,
|
|
local_block_ids: list[int],
|
|
kv_transfer_params: dict[str, Any] | None = None,
|
|
**kwargs: Any,
|
|
) -> None:
|
|
super().add_new_req(request_id, local_block_ids, kv_transfer_params, **kwargs)
|
|
|
|
kv_transfer_params = kv_transfer_params or {}
|
|
load_remote_cache = kv_transfer_params.get(
|
|
"do_remote_prefill",
|
|
kv_transfer_params.get("load_remote_cache", False),
|
|
)
|
|
|
|
if load_remote_cache:
|
|
remote_request_id = kv_transfer_params.get("remote_request_id", request_id)
|
|
meta = PatchedRecvReqMeta(
|
|
request_id=request_id,
|
|
remote_request_id=remote_request_id,
|
|
local_block_ids=local_block_ids,
|
|
kv_transfer_params=kv_transfer_params,
|
|
)
|
|
if not hasattr(self, "_reqs_need_recv"):
|
|
self._reqs_need_recv = {}
|
|
self._reqs_need_recv[request_id] = meta
|
|
|
|
def group_kv_pull(self, metadata: Any | None = None) -> None:
|
|
"""Use remote_request_id as ZMQ lookup key via save-patch-restore."""
|
|
if not hasattr(self, "_reqs_need_recv") or not self._reqs_need_recv:
|
|
return
|
|
|
|
original_recv = self._reqs_need_recv.copy()
|
|
patched_recv: dict[str, Any] = {}
|
|
|
|
for local_id, meta in original_recv.items():
|
|
if isinstance(meta, PatchedRecvReqMeta):
|
|
remote_id = meta.remote_request_id
|
|
self.remote_to_local_req[remote_id] = local_id
|
|
patched_meta = type(meta)(
|
|
request_id=remote_id,
|
|
remote_request_id=remote_id,
|
|
local_block_ids=meta.local_block_ids,
|
|
kv_transfer_params=meta.kv_transfer_params,
|
|
)
|
|
patched_recv[remote_id] = patched_meta
|
|
else:
|
|
patched_recv[local_id] = meta
|
|
|
|
self._reqs_need_recv = patched_recv
|
|
super().group_kv_pull(metadata)
|
|
|
|
# Restore unconsumed entries to original local keys
|
|
for remote_id, local_id in list(self.remote_to_local_req.items()):
|
|
if remote_id in self._reqs_need_recv:
|
|
entry = self._reqs_need_recv.pop(remote_id)
|
|
self._reqs_need_recv[local_id] = original_recv.get(local_id, entry)
|
|
|
|
def receive_kv(self, path: Any = None, req_blocks: Any = None) -> Any:
|
|
result = super().receive_kv(path, req_blocks)
|
|
|
|
if self.remote_to_local_req:
|
|
completed = [
|
|
rid
|
|
for rid, lid in self.remote_to_local_req.items()
|
|
if not hasattr(self, "_reqs_need_recv") or lid not in self._reqs_need_recv
|
|
]
|
|
for remote_id in completed:
|
|
self.remote_to_local_req.pop(remote_id, None)
|
|
|
|
return result
|
|
|
|
PatchedMooncakeConnector.__qualname__ = _OriginalMooncakeConnector.__qualname__
|
|
|
|
return PatchedMooncakeConnector
|
|
|
|
|
|
def apply_mooncake_connector_patch() -> bool:
|
|
"""Replace vLLM's MooncakeConnector with the patched version."""
|
|
global _patched
|
|
if _patched:
|
|
return True
|
|
|
|
_mc_module = _import_mooncake_module()
|
|
if _mc_module is None:
|
|
logger.warning("[monkey_patch] Cannot import MooncakeConnector — patch NOT applied.")
|
|
return False
|
|
|
|
_OriginalClass = _mc_module.MooncakeConnector
|
|
|
|
PatchedClass = _create_patched_mooncake_connector()
|
|
|
|
_mc_module.MooncakeConnector = PatchedClass
|
|
# Snapshot sys.modules: hasattr() may trigger lazy submodule imports that
|
|
# mutate sys.modules during iteration.
|
|
for _, module in list(sys.modules.items()):
|
|
if hasattr(module, "MooncakeConnector") and module.MooncakeConnector is _OriginalClass:
|
|
module.MooncakeConnector = PatchedClass
|
|
|
|
_patched = True
|
|
logger.info("[monkey_patch] MooncakeConnector patch applied")
|
|
return True
|