Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 96 additions & 1 deletion tests/v1/attention/test_b12x_sparse_mla_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
_select_sparse_components,
)
from vllm.models.deepseek_v32.b12x import B12xDeepseekV32Indexer
from vllm.platforms.interface import DeviceCapability
from vllm.platforms.interface import DeviceCapability, Platform
from vllm.v1.attention.backends.b12x import B12xPagedAttentionBackend
from vllm.v1.attention.backends.mla import b12x_indexer as generic_b12x_indexer
from vllm.v1.attention.backends.mla import b12x_mla_sparse
Expand Down Expand Up @@ -199,6 +199,101 @@ def test_b12x_glm5_next_keeps_hybrid_manager_page_unsplit() -> None:
)


def test_glm5_next_split_cache_auto_aligns_to_dcp_retention(monkeypatch) -> None:
config = SimpleNamespace(
model_config=SimpleNamespace(
architecture="Glm5NextForConditionalGeneration",
),
parallel_config=SimpleNamespace(decode_context_parallel_size=4),
cache_config=SimpleNamespace(
block_size=256,
mamba_block_size=None,
mamba_cache_mode="align",
mamba_page_size_padded=1234,
prefix_cache_retention_interval=4096,
),
)
monkeypatch.setenv("VLLM_GLM53_SPLIT_TARGET_BLOCK_SIZE", "auto")
monkeypatch.setenv("VLLM_GLM53_SPLIT_MAMBA_BLOCK_SIZE", "auto")

Platform._align_hybrid_block_size(config, B12xGLM5NextMLASparseBackend)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

assert config.cache_config.block_size == 1024
assert config.cache_config.mamba_block_size == 1024
assert config.cache_config.mamba_page_size_padded is None


@pytest.mark.parametrize(
(
"dcp",
"retention_interval",
"scheduled_tokens",
"batched_tokens",
"expected_block_size",
),
[
(1, None, None, 4096, 4096),
(2, None, None, 4096, 2048),
(4, None, None, 4096, 1024),
(8, None, None, 4096, 512),
(4, 0, None, 4096, 1024),
(4, 0, 4096, 4352, 1024),
],
)
def test_glm5_next_split_cache_auto_falls_back_to_scheduler_budget(
monkeypatch,
dcp: int,
retention_interval: int | None,
scheduled_tokens: int | None,
batched_tokens: int,
expected_block_size: int,
) -> None:
config = SimpleNamespace(
model_config=SimpleNamespace(
architecture="Glm5NextForConditionalGeneration",
),
parallel_config=SimpleNamespace(decode_context_parallel_size=dcp),
scheduler_config=SimpleNamespace(
max_num_scheduled_tokens=scheduled_tokens,
max_num_batched_tokens=batched_tokens,
),
cache_config=SimpleNamespace(
block_size=256,
mamba_block_size=None,
mamba_cache_mode="align",
mamba_page_size_padded=1234,
prefix_cache_retention_interval=retention_interval,
),
)
monkeypatch.setenv("VLLM_GLM53_SPLIT_TARGET_BLOCK_SIZE", "auto")
monkeypatch.setenv("VLLM_GLM53_SPLIT_MAMBA_BLOCK_SIZE", "auto")

Platform._align_hybrid_block_size(config, B12xGLM5NextMLASparseBackend)

assert config.cache_config.block_size == expected_block_size
assert config.cache_config.mamba_block_size == expected_block_size
assert config.cache_config.mamba_page_size_padded is None


def test_glm5_next_split_cache_auto_requires_dcp_aligned_retention(
monkeypatch,
) -> None:
config = SimpleNamespace(
model_config=SimpleNamespace(
architecture="Glm5NextForConditionalGeneration",
),
parallel_config=SimpleNamespace(decode_context_parallel_size=4),
cache_config=SimpleNamespace(
mamba_cache_mode="align",
prefix_cache_retention_interval=4097,
),
)
monkeypatch.setenv("VLLM_GLM53_SPLIT_TARGET_BLOCK_SIZE", "auto")

with pytest.raises(ValueError, match="divisible by decode_context_parallel_size"):
Platform._align_hybrid_block_size(config, B12xGLM5NextMLASparseBackend)


def test_b12x_glm5_next_rejects_unaligned_dcp(monkeypatch) -> None:
monkeypatch.setattr(b12x_mla_sparse, "get_b12x_sparse_mla", lambda: object())
with set_current_vllm_config(_glm5_next_config(dcp_size=2)):
Expand Down
49 changes: 47 additions & 2 deletions tests/v1/core/test_kv_cache_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2362,6 +2362,50 @@ def test_glm5next_split_cache_preserves_physical_pages(
)


def test_glm5next_nvfp4_auto_geometry_capacity() -> None:
"""DCP4 4K retention geometry recovers the expected 12.67M capacity."""
target = MLAAttentionSpec(
block_size=1024,
num_kv_heads=1,
head_size=512,
dtype=torch.uint8,
cache_dtype_str="nvfp4_ds_mla",
state_content_bytes=304,
model_version="glm5_next",
page_tail_bytes_per_token=33,
alignment=64 * 132,
)
recurrent = MambaSpec(
block_size=1024,
shapes=((1_085_440,),),
dtypes=(torch.uint8,),
mamba_cache_mode="align",
num_prefill_checkpoint_blocks=1,
)
groups = [
KVCacheGroupSpec([f"recurrent.{i}.{j}" for j in range(width)], recurrent)
for i, width in enumerate((9, 9, 8, 8))
]
groups.append(KVCacheGroupSpec([f"target.{i}" for i in range(11)], target))
config = KVCacheConfig(
num_blocks=3873,
kv_cache_tensors=[],
kv_cache_groups=groups,
prefix_cache_retention_interval=4096,
)
vllm_config = SimpleNamespace(
model_config=SimpleNamespace(max_model_len=202_752),
parallel_config=SimpleNamespace(decode_context_parallel_size=4),
cache_config=SimpleNamespace(mamba_cache_mode="align"),
)

capacity, concurrency = get_kv_cache_capacity(vllm_config, config)

assert target.page_size_bytes == 346_368
assert capacity == 12_665_459
assert concurrency == pytest.approx(62.46774193548387)


def new_indexer_mla_spec(block_size=16):
# Sparse-attention indexer k_cache: an MLAAttentionSpec with a much smaller
# page size than the main MLA attention (uint8, small head), so their pages
Expand Down Expand Up @@ -2552,8 +2596,9 @@ def test_dflash_draft_cache_partition_is_pp1_only():
attention_backend="FLASH_ATTN",
),
model_config=SimpleNamespace(
get_num_layers=lambda parallel_config,
target_layers=target_layers: target_layers
get_num_layers=lambda parallel_config, target_layers=target_layers: (
target_layers
)
),
parallel_config=SimpleNamespace(
pipeline_parallel_size=pipeline_parallel_size
Expand Down
56 changes: 43 additions & 13 deletions vllm/platforms/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -814,22 +814,52 @@ def _align_hybrid_block_size(
"VLLM_GLM53_SPLIT_TARGET_BLOCK_SIZE is supported only for "
"Glm5NextForConditionalGeneration."
)
target_block_size = int(split_target_block_size)
split_mamba_block_size = int(
os.getenv(
"VLLM_GLM53_SPLIT_MAMBA_BLOCK_SIZE",
split_target_block_size,
)
if split_target_block_size.lower() == "auto":
geometry_interval = cache_config.prefix_cache_retention_interval
# ``0`` is a valid semantic-only retention policy, but it does
# not provide a periodic geometry interval. In that mode (and
# when retention is unset), derive the block from the worker's
# scheduler budget instead.
if geometry_interval is None or geometry_interval == 0:
scheduler_config = vllm_config.scheduler_config
geometry_interval = scheduler_config.max_num_scheduled_tokens
if geometry_interval is None:
geometry_interval = scheduler_config.max_num_batched_tokens
dcp_world_size = parallel_config.decode_context_parallel_size
if (
geometry_interval is None
or geometry_interval <= 0
or geometry_interval % dcp_world_size != 0
):
raise ValueError(
"Automatic GLM-5.3 split-cache geometry requires a "
"positive prefix_cache_retention_interval or scheduler "
"token budget divisible by decode_context_parallel_size."
)
# A DCP-sharded target block covers block_size * DCP global
# tokens. Fill one retention interval, or one scheduler token
# budget when no external-cache retention policy is active,
# with exactly one target block per rank. This makes packed
# NVFP4 pages use the shared pool efficiently while connector
# stores still land on whole pages when retention is enabled.
target_block_size = geometry_interval // dcp_world_size
else:
target_block_size = int(split_target_block_size)

split_mamba_block_size = os.getenv(
"VLLM_GLM53_SPLIT_MAMBA_BLOCK_SIZE", "auto"
)
mamba_block_size = (
target_block_size
if split_mamba_block_size.lower() == "auto"
else int(split_mamba_block_size)
)
if target_block_size <= 0 or target_block_size % 64 != 0:
raise ValueError(
"VLLM_GLM53_SPLIT_TARGET_BLOCK_SIZE must be a positive "
"multiple of 64."
"multiple of 64 after automatic resolution."
)
if (
split_mamba_block_size <= 0
or split_mamba_block_size % target_block_size != 0
):
if mamba_block_size <= 0 or mamba_block_size % target_block_size != 0:
raise ValueError(
"VLLM_GLM53_SPLIT_MAMBA_BLOCK_SIZE must be a positive "
"multiple of VLLM_GLM53_SPLIT_TARGET_BLOCK_SIZE."
Expand All @@ -844,13 +874,13 @@ def _align_hybrid_block_size(
# physical pages. Their token block sizes remain scheduler-visible,
# so the recurrent block must be a multiple of the target block.
cache_config.block_size = target_block_size
cache_config.mamba_block_size = split_mamba_block_size
cache_config.mamba_block_size = mamba_block_size
cache_config.mamba_page_size_padded = None
logger.warning(
"Using split GLM-5.3 cache pages: target block size %d tokens, "
"recurrent-state block size %d tokens.",
target_block_size,
split_mamba_block_size,
mamba_block_size,
)
return

Expand Down
Loading