class AliceAIDecoderBlock(nn.Module):
"""AliceAI topology with upstream Qwen, Kimi KDA, MoE and AttnRes operations."""
def __init__(
self,
vllm_config: VllmConfig,
layer_type: str,
prefix: str = "",
) -> None:
super().__init__()
config = vllm_config.model_config.hf_text_config
model_config = vllm_config.model_config
cache_config = vllm_config.cache_config
parallel_config = vllm_config.parallel_config
quant_config = vllm_config.quant_config
if config.num_experts <= 0:
raise ValueError("AliceAI requires sparse experts")
if parallel_config.use_sequence_parallel_moe:
raise NotImplementedError("AliceAI does not support sequence-parallel MoE")
self.layer_type = layer_type
self.layer_idx = extract_layer_index(prefix)
self.attn_res_block_size = get_attn_res_block_size(config)
self.attn_res_rms_norm_eps = float(config.rms_norm_eps)
if layer_type == "linear_attention":
self.linear_attn = KimiGatedDeltaNetAttention(
config=_make_kimi_kda_config(config),
vllm_config=vllm_config,
prefix=f"{prefix}.linear_attn",
)
self.linear_attn.o_norm.eps = float(config.rms_norm_eps)
elif layer_type == "full_attention":
self.self_attn = Qwen3NextAttention(
config=_make_alice_ai_full_attention_config(config),
model_config=model_config,
cache_config=cache_config,
quant_config=quant_config,
prefix=f"{prefix}.self_attn",
)
else:
raise ValueError(f"Invalid layer_type {layer_type}")
self.mlp = AliceAISparseMoeBlock(
vllm_config=vllm_config,
prefix=f"{prefix}.mlp",
)
self.input_layernorm = GemmaRMSNorm(
hidden_size=config.hidden_size, eps=config.rms_norm_eps
)
self.post_attention_layernorm = GemmaRMSNorm(
hidden_size=config.hidden_size, eps=config.rms_norm_eps
)
if self.layer_idx > 0:
self.attn_res_proj = ReplicatedLinear(
input_size=config.hidden_size,
output_size=1,
bias=False,
prefix=f"{prefix}.attn_res_proj",
)
self.attn_res_norm_weight = nn.Parameter(torch.ones(config.hidden_size))
self.mlp_res_proj = ReplicatedLinear(
input_size=config.hidden_size,
output_size=1,
bias=False,
prefix=f"{prefix}.mlp_res_proj",
)
self.mlp_res_norm_weight = nn.Parameter(torch.ones(config.hidden_size))
def _apply_attention_residual(
self,
residual_bank: torch.Tensor,
num_completed_sources: int,
partial: torch.Tensor | None,
*,
projection: ReplicatedLinear,
norm_weight_name: str,
) -> torch.Tensor:
if partial is None:
if num_completed_sources < 1:
raise RuntimeError("AttnRes requires at least one residual source")
prefix = residual_bank[:, num_completed_sources - 1]
num_blocks = num_completed_sources - 1
else:
prefix = partial
num_blocks = num_completed_sources
return attn_res(
prefix=prefix,
delta=None,
blocks=residual_bank,
norm_weight=getattr(self, norm_weight_name),
qk_weight=projection.weight.squeeze(0),
output_norm_weight=None,
num_blocks=num_blocks,
block_write_idx=-1,
eps=self.attn_res_rms_norm_eps,
output_norm_eps=0.0,
)
def _apply_attention(
self, hidden_states: torch.Tensor, positions: torch.Tensor
) -> torch.Tensor:
if self.layer_type == "linear_attention":
return self.linear_attn(hidden_states=hidden_states, positions=positions)
return self.self_attn(hidden_states=hidden_states, positions=positions)
def forward_attn_res(
self,
positions: torch.Tensor,
residual_bank: torch.Tensor,
num_completed_sources: int,
partial: torch.Tensor | None,
) -> tuple[int, torch.Tensor]:
if self.layer_idx > 0 and self.layer_idx % self.attn_res_block_size == 0:
if partial is None:
raise RuntimeError("cannot finalize an empty AttnRes block")
residual_bank[:, num_completed_sources].copy_(partial)
num_completed_sources += 1
partial = None
if self.layer_idx == 0:
hidden_states = residual_bank[:, 0]
else:
hidden_states = self._apply_attention_residual(
residual_bank=residual_bank,
num_completed_sources=num_completed_sources,
partial=partial,
projection=self.attn_res_proj,
norm_weight_name="attn_res_norm_weight",
)
hidden_states = self.input_layernorm(hidden_states)
attention_output = self._apply_attention(
hidden_states=hidden_states, positions=positions
)
partial = attention_output if partial is None else partial + attention_output
hidden_states = self._apply_attention_residual(
residual_bank=residual_bank,
num_completed_sources=num_completed_sources,
partial=partial,
projection=self.mlp_res_proj,
norm_weight_name="mlp_res_norm_weight",
)
hidden_states = self.post_attention_layernorm(hidden_states)
mlp_output = self.mlp(hidden_states)
return num_completed_sources, partial + mlp_output