vLLM 0.29 removed AttentionSpec.indexes_kv_by_block_stride and reshaped KVCacheTensor from (size, shared_by) to an explicit layout: layers in L order plus layer_stride and block_stride. The block-stride decision now lives on one physical CacheConfig.kv_cache_layout that the engine core resolves once, and layout.is_block_outermost is the direct successor of the old per-spec flag. Diffusion backends subclass the Omni AttentionBackend ABC, so upstream get_supported_kv_cache_layouts never sees them and the engine core never resolves a layout for them. Add diffusion_kv/layout.py to pin it before get_kv_cache_configs runs, defaulting to LBNHC, which reproduces the indexes_kv_by_block_stride=False that every dense diffusion backend declared. The backend hook is kept as an enforced contract rather than deleted: a backend needing block-stride indexing selects a block-outermost layout, and a contradiction raises instead of mis-addressing K/V. Workers receive only a KVCacheConfig, so the resolved name travels on it (KVCacheConfig.kv_cache_layout) and is adopted into each worker CacheConfig before allocation. Without that, init_kv_cache raises in the worker because get_resolved_kv_cache_layout has nothing to return. init_kv_cache also lost attn_groups and cache_dtype in 0.29; the production caller in model_runner_backend and the test stubs move to the six-argument form. Tensor construction goes through one derivation built on the upstream compute_layout_strides helper, shared by production and tests, so a wrong stride cannot be masked by a parallel test-only copy. Also updates the sizing expectation: 0.29 subtracts the null block that BlockPool permanently holds back before estimating, so sixteen physical pages now yield fifteen usable ones. One review point is addressed only in part, deliberately. build_native_kv_cache_configs now takes indexes_kv_by_block_stride and propagates a block-outermost layout end to end when it is set, with a test covering control plane to worker adoption. Automatic discovery of that requirement from the workers is not added: 0.29 removed the field that used to carry it, every diffusion backend in tree declares False, and inventing an executor RPC for a capability nothing requests would ship untestable plumbing. Until a backend needs it, the seam is explicit and the mismatch raises at worker init rather than corrupting addressing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146Xu5Sgj4vXxLxvLjV5YPj Signed-off-by: tzhouam <tzhouam@connect.ust.hk>
111 lines
4.7 KiB
Python
111 lines
4.7 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
import torch
|
|
from vllm.config import VllmConfig
|
|
from vllm.v1.core.kv_cache_utils import (
|
|
generate_scheduler_kv_cache_config,
|
|
get_kv_cache_configs,
|
|
resolve_kv_cache_block_sizes,
|
|
)
|
|
from vllm.v1.kv_cache_interface import KVCacheConfig, KVCacheSpec
|
|
|
|
from vllm_omni.diffusion.data import OmniDiffusionConfig
|
|
from vllm_omni.diffusion.diffusion_kv.config import DiffusionKVCacheMode
|
|
from vllm_omni.diffusion.diffusion_kv.layout import resolve_diffusion_kv_cache_layout
|
|
from vllm_omni.diffusion.vllm_config import create_diffusion_vllm_config
|
|
from vllm_omni.platforms import current_omni_platform
|
|
|
|
if TYPE_CHECKING:
|
|
from vllm_omni.diffusion.executor.abstract import DiffusionExecutor
|
|
from vllm_omni.diffusion.request import OmniDiffusionRequest
|
|
|
|
|
|
def build_native_kv_cache_configs(
|
|
vllm_config: VllmConfig,
|
|
worker_specs: list[dict[str, KVCacheSpec]],
|
|
available_memory: list[int],
|
|
*,
|
|
indexes_kv_by_block_stride: bool = False,
|
|
) -> tuple[list[KVCacheConfig], KVCacheConfig]:
|
|
"""Build rank-local and Scheduler-native configs using vLLM utilities.
|
|
|
|
``indexes_kv_by_block_stride`` carries the native backend requirement that
|
|
0.29 removed from ``AttentionSpec``. Every diffusion backend in tree
|
|
declares ``False``, so the default reproduces current behaviour; a caller
|
|
that learns otherwise from its workers passes ``True`` here and the
|
|
block-outermost layout propagates to every rank-local config.
|
|
"""
|
|
|
|
from vllm.v1.core import single_type_kv_cache_manager
|
|
|
|
# 0.29 sizes every KVCacheTensor region from the resolved physical layout,
|
|
# so it must be pinned before get_kv_cache_configs. Idempotent, so callers
|
|
# that already resolved it (the control plane) are unaffected.
|
|
layout = resolve_diffusion_kv_cache_layout(
|
|
vllm_config,
|
|
indexes_kv_by_block_stride=indexes_kv_by_block_stride,
|
|
)
|
|
single_type_kv_cache_manager.register_all_kvcache_specs(vllm_config)
|
|
worker_configs = get_kv_cache_configs(vllm_config, worker_specs, available_memory)
|
|
scheduler_config = generate_scheduler_kv_cache_config(worker_configs)
|
|
# Workers only receive a KVCacheConfig, so the resolved layout travels on it
|
|
# and is adopted into each worker's own CacheConfig before allocation.
|
|
for config in (*worker_configs, scheduler_config):
|
|
config.kv_cache_layout = layout.name
|
|
return worker_configs, scheduler_config
|
|
|
|
|
|
def initialize_diffusion_kv_control_plane(
|
|
executor: DiffusionExecutor,
|
|
od_config: OmniDiffusionConfig,
|
|
*,
|
|
profile_requests: list[OmniDiffusionRequest] | None = None,
|
|
device: torch.device | None = None,
|
|
) -> tuple[KVCacheConfig, int, int, VllmConfig] | None:
|
|
"""Run the native Worker-spec to Scheduler-config initialization chain."""
|
|
|
|
if (
|
|
getattr(od_config, "diffusion_kv_mode", DiffusionKVCacheMode.DENSE_LEGACY)
|
|
is not DiffusionKVCacheMode.PAGED_SCHEDULER
|
|
):
|
|
return None
|
|
if not profile_requests:
|
|
raise ValueError("paged_scheduler Diffusion KV initialization requires prepared memory profile requests")
|
|
if device is None:
|
|
device = current_omni_platform.get_torch_device(0)
|
|
vllm_config = create_diffusion_vllm_config(device, od_config)
|
|
worker_specs = executor.get_kv_cache_specs()
|
|
available_memory = executor.determine_available_kv_memory(profile_requests)
|
|
if len(worker_specs) != od_config.num_gpus or len(available_memory) != od_config.num_gpus:
|
|
raise ValueError(
|
|
"Diffusion KV initialization rank count mismatch: "
|
|
f"expected={od_config.num_gpus}, specs={len(worker_specs)}, memory={len(available_memory)}"
|
|
)
|
|
worker_configs, scheduler_kv_cache_config = build_native_kv_cache_configs(
|
|
vllm_config,
|
|
worker_specs,
|
|
available_memory,
|
|
)
|
|
scheduler_block_size, hash_block_size = resolve_kv_cache_block_sizes(
|
|
scheduler_kv_cache_config,
|
|
vllm_config,
|
|
)
|
|
# Native sizing may auto-fit ``max_model_len=-1`` against the profiled
|
|
# memory budget. Forward that final value so every Worker sizes its
|
|
# physical BlockTables from the same bound as the Scheduler.
|
|
resolved_max_model_len = vllm_config.model_config.max_model_len
|
|
if type(resolved_max_model_len) is not int or resolved_max_model_len <= 0:
|
|
raise ValueError("native Diffusion KV sizing must resolve a positive max_model_len")
|
|
executor.set_kv_cache_configs(worker_configs, resolved_max_model_len)
|
|
return (
|
|
scheduler_kv_cache_config,
|
|
scheduler_block_size,
|
|
hash_block_size,
|
|
vllm_config,
|
|
)
|