Fix attention backends for models with per-layer head counts (num_attention_heads_per_layer) (#32625)

This commit is contained in:
Jimmy Shong
2026-07-29 20:03:00 -07:00
committed by GitHub
parent 22faf9fef8
commit ed361ae7f0
12 changed files with 49 additions and 4 deletions
@@ -1093,6 +1093,14 @@ class ModelConfig:
# equal to the number of attention heads.
return self.hf_text_config.num_attention_heads
def get_max_num_attention_heads(self) -> int:
"""Max per-layer query head count; num_attention_heads unless the
model sets num_attention_heads_per_layer."""
per_layer = getattr(self.hf_text_config, "num_attention_heads_per_layer", None)
if per_layer:
return max(per_layer)
return self.num_attention_heads
def get_num_kv_heads(self, tensor_parallel_size) -> int:
"""Returns the number of KV heads per GPU."""
total_num_kv_heads = self.get_total_num_kv_heads()
@@ -1468,8 +1468,12 @@ class FlashInferAttnBackend(AttentionBackend):
class FlashInferIndicesUpdaterDecode:
def __init__(self, model_runner: ModelRunner, attn_backend: FlashInferAttnBackend):
# Parse Constants
# Plan with the max per-layer head count: FlashInfer bakes num_qo_heads
# into the plan, and layers running more heads than planned are
# silently corrupted. Over-planning is safe.
self.num_qo_heads = (
model_runner.model_config.num_attention_heads // get_parallel().attn_tp_size
model_runner.model_config.get_max_num_attention_heads()
// get_parallel().attn_tp_size
)
self.num_kv_heads = model_runner.model_config.get_num_kv_heads(
get_parallel().attn_tp_size
@@ -1736,8 +1740,12 @@ class FlashInferIndicesUpdaterDecode:
class FlashInferIndicesUpdaterPrefill:
def __init__(self, model_runner: ModelRunner, attn_backend: FlashInferAttnBackend):
# Parse Constants
# Plan with the max per-layer head count: FlashInfer bakes num_qo_heads
# into the plan, and layers running more heads than planned are
# silently corrupted. Over-planning is safe.
self.num_qo_heads = (
model_runner.model_config.num_attention_heads // get_parallel().attn_tp_size
model_runner.model_config.get_max_num_attention_heads()
// get_parallel().attn_tp_size
)
self.num_kv_heads = model_runner.model_config.get_num_kv_heads(
get_parallel().attn_tp_size
@@ -178,7 +178,8 @@ class TritonAttnBackend(AttentionBackend):
self.dcp_size = get_parallel().attn_dcp_size
self.dcp_rank = get_parallel().attn_dcp_rank
self.num_head = (
model_runner.model_config.num_attention_heads // get_parallel().attn_tp_size
model_runner.model_config.get_max_num_attention_heads()
// get_parallel().attn_tp_size
) * self.dcp_size
self.num_kv_head = model_runner.model_config.get_num_kv_heads(
get_parallel().attn_tp_size
@@ -1858,7 +1859,8 @@ class TritonMultiStepDraftBackend:
)
self.max_context_len = self.attn_backends[0].max_context_len
self.num_head = (
model_runner.model_config.num_attention_heads // get_parallel().attn_tp_size
model_runner.model_config.get_max_num_attention_heads()
// get_parallel().attn_tp_size
)
self.device = model_runner.device
# Cached variables for generate_draft_decode_kv_indices
@@ -295,6 +295,9 @@ class TinyModelConfig:
assert self.num_attention_heads % tp_size == 0
return self.num_attention_heads // tp_size
def get_max_num_attention_heads(self) -> int:
return self.num_attention_heads
def get_num_kv_heads(self, tp_size: int) -> int:
assert self.num_key_value_heads % tp_size == 0
return self.num_key_value_heads // tp_size
@@ -263,6 +263,9 @@ class TinyDSAModelConfig:
self.hf_text_config = self.hf_config
self.linear_attn_registry_result = None
def get_max_num_attention_heads(self) -> int:
return self.num_attention_heads
class DSAMockModelRunner(ModelRunner):
def __init__(
@@ -287,6 +287,9 @@ class TinyDSV4ModelConfig:
self.hf_text_config = self.hf_config
self.linear_attn_registry_result = None
def get_max_num_attention_heads(self) -> int:
return self.num_attention_heads
class MockDSV4ModelRunner:
"""Minimal runner exposing what `DeepseekV4AttnBackend.__init__` reads.
@@ -295,6 +295,9 @@ class TinyDualChunkModelConfig:
assert self.num_attention_heads % tp_size == 0
return self.num_attention_heads // tp_size
def get_max_num_attention_heads(self) -> int:
return self.num_attention_heads
def get_num_kv_heads(self, tp_size: int) -> int:
assert self.num_key_value_heads % tp_size == 0
return self.num_key_value_heads // tp_size
@@ -188,6 +188,9 @@ class TinyGDNModelConfig:
self.hf_text_config = self.hf_config
self.linear_attn_registry_result = None
def get_max_num_attention_heads(self) -> int:
return self.num_attention_heads
def get_num_kv_heads(self, tp_size: int) -> int:
assert self.num_key_value_heads % tp_size == 0
return self.num_key_value_heads // tp_size
@@ -193,6 +193,9 @@ class TinyKDAModelConfig:
self.hf_text_config = self.hf_config
self.linear_attn_registry_result = None
def get_max_num_attention_heads(self) -> int:
return self.num_attention_heads
def get_num_kv_heads(self, tp_size: int) -> int:
assert self.num_key_value_heads % tp_size == 0
return self.num_key_value_heads // tp_size
@@ -203,6 +203,9 @@ class TinyLightningModelConfig:
self.hf_text_config = self.hf_config
self.linear_attn_registry_result = None
def get_max_num_attention_heads(self) -> int:
return self.num_attention_heads
def get_num_kv_heads(self, tp_size: int) -> int:
assert self.num_key_value_heads % tp_size == 0
return self.num_key_value_heads // tp_size
@@ -289,6 +289,9 @@ class TinyMamba2ModelConfig:
self.hf_text_config = self.hf_config
self.linear_attn_registry_result = None
def get_max_num_attention_heads(self) -> int:
return self.num_attention_heads
def get_num_kv_heads(self, tp_size: int) -> int:
assert self.num_key_value_heads % tp_size == 0
return self.num_key_value_heads // tp_size
@@ -200,6 +200,9 @@ class TinyMLAModelConfig:
assert self.num_attention_heads % tp_size == 0
return self.num_attention_heads // tp_size
def get_max_num_attention_heads(self) -> int:
return self.num_attention_heads
def get_num_kv_heads(self, tp_size: int) -> int:
return 1