Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

521 lines
20 KiB
Python

"""EAGLE3 / EAGLE3.1 draft model with MLA attention for Kimi-K2.x.
The ``kimi-k2.5-eagle3-mla`` checkpoint pairs an EAGLE3 layout
(concatenated [embed_norm, hidden_norm] pre-attention input, fc projection
over the concatenated multi-layer aux hidden states, single decoder layer,
dense MLP) with DeepSeek-V2 multi-latent attention. Sharing the MLA layout
with the Kimi-K2.x target keeps the draft KV cache small.
The eagle3.1 variant (e.g. ``kimi-k2.6-eagle3.1-mla``) adds two optional
config flags on top of the same layout:
* ``fc_norm``: per-chunk RMSNorm applied to each auxiliary hidden state
before the fc projection.
* ``norm_output``: emit post-norm (rather than pre-norm) hidden states as
the auxiliary output consumed by the next draft step.
"""
import copy
import logging
import re
from typing import Iterable, List, Optional, Tuple
import torch
from torch import nn
from transformers import PretrainedConfig
from sglang.srt.distributed import get_pp_group
from sglang.srt.distributed.device_communicators import triton_symm_mem_ag
from sglang.srt.layers.communicator import AttentionInputs, get_attn_tp_context
from sglang.srt.layers.layernorm import RMSNorm
from sglang.srt.layers.linear import ColumnParallelLinear, ReplicatedLinear
from sglang.srt.layers.logits_processor import LogitsProcessor
from sglang.srt.layers.quantization.base_config import QuantizationConfig
from sglang.srt.layers.vocab_parallel_embedding import (
ParallelLMHead,
VocabParallelEmbedding,
get_embedding_tp_kwargs,
)
from sglang.srt.model_executor.forward_batch_info import ForwardBatch, PPProxyTensors
from sglang.srt.model_loader.weight_utils import default_weight_loader
from sglang.srt.models.deepseek_v2 import DeepseekV2AttentionMLA, DeepseekV2MLP
from sglang.srt.utils import BumpAllocator, add_prefix
logger = logging.getLogger(__name__)
def _get_eagle_aux_layer_count(config: PretrainedConfig) -> int:
"""Number of target layers whose hidden states get concatenated into fc."""
eagle_config = getattr(config, "eagle_config", None)
if isinstance(eagle_config, dict):
layer_ids = eagle_config.get("eagle_aux_hidden_state_layer_ids")
else:
layer_ids = getattr(eagle_config, "eagle_aux_hidden_state_layer_ids", None)
if layer_ids is None:
layer_ids = getattr(config, "eagle_aux_hidden_state_layer_ids", None)
if layer_ids is None:
return 3
return len(layer_ids)
class Eagle3MLADecoderLayer(nn.Module):
"""One EAGLE3 draft layer that uses DeepSeek-V2 multi-latent attention.
Pre-attention concatenates the input embedding and the target hidden
state along the channel dim, doubling the input width to MLA's fused
QKV-down projection.
"""
def __init__(
self,
config: PretrainedConfig,
layer_id: int = 0,
quant_config: Optional[QuantizationConfig] = None,
prefix: str = "",
) -> None:
super().__init__()
self.hidden_size = config.hidden_size
if hasattr(config, "rope_parameters") and config.rope_parameters is not None:
rope_params = config.rope_parameters
rope_theta = rope_params.get("rope_theta", 10000)
rope_scaling = (
rope_params if rope_params.get("rope_type") != "default" else None
)
else:
rope_theta = config.rope_theta
rope_scaling = config.rope_scaling
max_position_embeddings = config.max_position_embeddings
self.self_attn = DeepseekV2AttentionMLA(
config=config,
hidden_size=config.hidden_size,
num_heads=config.num_attention_heads,
qk_nope_head_dim=config.qk_nope_head_dim,
qk_rope_head_dim=config.qk_rope_head_dim,
v_head_dim=config.v_head_dim,
q_lora_rank=config.q_lora_rank,
kv_lora_rank=config.kv_lora_rank,
rope_theta=rope_theta,
rope_scaling=rope_scaling,
max_position_embeddings=max_position_embeddings,
quant_config=quant_config,
layer_id=layer_id,
reduce_results=True,
prefix=add_prefix("self_attn", prefix),
)
# EAGLE3 doubles MLA's QKV-down input by concatenating
# input_layernorm(embed) and hidden_norm(target_hidden) along the
# feature dim. Replace the projection that DeepseekV2AttentionMLA
# built for a single-hidden input.
attn = self.self_attn
if attn.q_lora_rank is None:
raise ValueError(
"Eagle3 MLA layer requires q_lora_rank in the draft config"
)
attn.fused_qkv_a_proj_with_mqa = ReplicatedLinear(
2 * config.hidden_size,
attn.q_lora_rank + attn.kv_lora_rank + attn.qk_rope_head_dim,
bias=False,
quant_config=quant_config,
prefix=add_prefix("self_attn.fused_qkv_a_proj_with_mqa", prefix),
)
# Recompute fused-proj-dependent flags so they reflect the new input dim.
attn.has_fused_proj = True
attn.use_min_latency_fused_a_gemm = False
quant_method = getattr(attn.fused_qkv_a_proj_with_mqa, "quant_method", None)
attn.is_packed_weight = (
quant_method is not None
and hasattr(quant_method, "quant_config")
and quant_method.quant_config is not None
and quant_method.quant_config.get_name()
in {"awq", "awq_marlin", "moe_wna16"}
)
self.mlp = DeepseekV2MLP(
hidden_size=config.hidden_size,
intermediate_size=config.intermediate_size,
hidden_act=config.hidden_act,
quant_config=quant_config,
prefix=add_prefix("mlp", prefix),
)
self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
self.hidden_norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
self.post_attention_layernorm = RMSNorm(
config.hidden_size, eps=config.rms_norm_eps
)
def forward(
self,
positions: torch.Tensor,
embeds: torch.Tensor,
hidden_states: torch.Tensor,
forward_batch: ForwardBatch,
zero_allocator: BumpAllocator,
) -> Tuple[torch.Tensor, torch.Tensor]:
residual = hidden_states
embeds = self.input_layernorm(embeds)
hidden_states = self.hidden_norm(hidden_states)
attn_input = torch.cat([embeds, hidden_states], dim=-1)
# MLA's forward_absorb_prepare reads the qkv-down projection result
# from attn_tp_context. We bypass LayerCommunicator here (the eagle3
# draft layer is one isolated layer with custom pre-attention norms),
# so publish the attention input ourselves.
get_attn_tp_context().set_attn_inputs(
AttentionInputs(
attn_input,
forward_batch,
self.self_attn.prepare_qkv_latent,
)
)
attn_out = self.self_attn(
positions=positions,
hidden_states=attn_input,
forward_batch=forward_batch,
zero_allocator=zero_allocator,
)
if isinstance(attn_out, tuple):
attn_out = attn_out[0]
hidden_states, residual = self.post_attention_layernorm(attn_out, residual)
hidden_states = self.mlp(hidden_states)
return hidden_states, residual
class Eagle3MLAModel(nn.Module):
def __init__(
self,
config: PretrainedConfig,
quant_config: Optional[QuantizationConfig] = None,
prefix: str = "",
) -> None:
super().__init__()
self.config = config
self.vocab_size = config.vocab_size
self.embed_tokens = VocabParallelEmbedding(
config.vocab_size,
config.hidden_size,
prefix=add_prefix("embed_tokens", prefix),
**get_embedding_tp_kwargs(),
)
target_hidden_size = (
getattr(config, "target_hidden_size", None) or config.hidden_size
)
self.num_aux_hidden_states = _get_eagle_aux_layer_count(config)
self.fc = ColumnParallelLinear(
target_hidden_size * self.num_aux_hidden_states,
config.hidden_size,
bias=getattr(config, "bias", False),
gather_output=False,
quant_config=quant_config,
prefix=add_prefix("fc", prefix),
)
# Guarded multimem all-gather for the fc output; buffer covers prefill.
self._fc_gatherer = triton_symm_mem_ag.MultimemAllGatherer(
max_tokens=triton_symm_mem_ag.recommended_max_tokens(
include_prefill=True, floor=512
),
skip_entry_sync=False,
)
# Per-aux RMSNorm before fc; enabled via `fc_norm` or legacy
# `use_aux_norm` flag. Matches the eagle3.1 layout.
use_fc_norm = getattr(config, "fc_norm", None) or getattr(
config, "use_aux_norm", False
)
if use_fc_norm:
self.fc_norm = nn.ModuleList(
[
RMSNorm(target_hidden_size, eps=config.rms_norm_eps)
for _ in range(self.num_aux_hidden_states)
]
)
else:
self.fc_norm = None
if config.num_hidden_layers != 1:
raise ValueError("EAGLE3 currently only supports 1 layer")
self.midlayer = Eagle3MLADecoderLayer(
config,
layer_id=0,
quant_config=quant_config,
prefix=prefix,
)
self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
# Draft decode captures pre-norm hidden by default; eagle3.1 opts for
# post-norm via `norm_output: true`.
self.norm_output = getattr(config, "norm_output", False)
def forward(
self,
input_ids: torch.Tensor,
positions: torch.Tensor,
forward_batch: ForwardBatch,
input_embeds: torch.Tensor = None,
pp_proxy_tensors: Optional[PPProxyTensors] = None,
) -> Tuple[torch.Tensor, List[torch.Tensor]]:
if input_embeds is None:
# MM positions in input_ids hold MM_PAD_SHIFT_VALUE+hash sentinels (far above
# vocab_size). Use target-produced mm_input_embeds for these positions and
# only call embed_tokens on the appended next-token to avoid embed OOB.
embeds = forward_batch.mm_input_embeds
if (
forward_batch.forward_mode.is_extend()
and forward_batch.contains_mm_inputs()
and not forward_batch.forward_mode.is_draft_extend_v2()
):
assert embeds is not None
last_indices = (
forward_batch.extend_start_loc + forward_batch.extend_seq_lens - 1
).long()
embeds[last_indices] = self.embed_tokens(input_ids[last_indices])
if embeds is None:
embeds = self.embed_tokens(input_ids)
else:
embeds = input_embeds
hidden_states = forward_batch.spec_info.hidden_states
if hidden_states.shape[-1] != embeds.shape[-1]:
if self.fc_norm is not None:
chunks = hidden_states.chunk(self.num_aux_hidden_states, dim=-1)
hidden_states = torch.cat(
[norm(chunk) for norm, chunk in zip(self.fc_norm, chunks)],
dim=-1,
)
hidden_states, _ = self.fc(hidden_states)
hidden_states = self._fc_gatherer(hidden_states)
if hidden_states.shape[0] == 0:
return hidden_states, [hidden_states]
zero_allocator = BumpAllocator(
buffer_size=2,
dtype=torch.float32,
device=embeds.device,
)
hidden_states, residual = self.midlayer(
positions, embeds, hidden_states, forward_batch, zero_allocator
)
hidden_states_to_logits, hidden_states_to_aux = self.norm(
hidden_states, residual
)
aux = hidden_states_to_logits if self.norm_output else hidden_states_to_aux
return hidden_states_to_logits, [aux]
class Eagle3DeepseekV2ForCausalLM(nn.Module):
"""EAGLE3 draft model architecture with DeepSeek-V2 MLA attention.
Used by checkpoints like ``kimi-k2.5-eagle3-mla`` that pair
an EAGLE3 layout with multi-latent attention so the draft KV cache shape
matches the target's MLA cache.
"""
def __init__(
self,
config: PretrainedConfig,
quant_config: Optional[QuantizationConfig] = None,
prefix: str = "",
) -> None:
super().__init__()
self.config = config
# Match deepseek_nextn behavior: modelopt_fp4 is target-only and the
# bf16 draft must not inherit the FP4 quant method.
if quant_config is not None and quant_config.get_name() == "modelopt_fp4":
logger.warning(
"Overriding Eagle3DeepseekV2ForCausalLM quant config for "
"modelopt_fp4 target; draft weights are bf16."
)
quant_config = None
self.quant_config = quant_config
self.pp_group = get_pp_group()
self.model = Eagle3MLAModel(
config, quant_config=quant_config, prefix=add_prefix("model", prefix)
)
# llama_eagle3 sets a load-from-target flag when draft_vocab_size is
# missing. This checkpoint declares its own draft head, so keep ours.
self.load_lm_head_from_target = False
draft_vocab_size = getattr(config, "draft_vocab_size", None)
if config.tie_word_embeddings:
self.lm_head = self.model.embed_tokens
else:
if draft_vocab_size is None:
self.load_lm_head_from_target = True
draft_vocab_size = config.vocab_size
config.draft_vocab_size = draft_vocab_size
self.lm_head = ParallelLMHead(
draft_vocab_size,
config.hidden_size,
quant_config=quant_config,
prefix=add_prefix("lm_head", prefix),
)
# Logits processor sees the draft vocab.
config_for_logits = copy.deepcopy(config)
config_for_logits.vocab_size = draft_vocab_size or config.vocab_size
self.logits_processor = LogitsProcessor(config_for_logits)
self.capture_aux_hidden_states = True
self.hot_token_id = None
@torch.no_grad()
def forward(
self,
input_ids: torch.Tensor,
positions: torch.Tensor,
forward_batch: ForwardBatch,
input_embeds: torch.Tensor = None,
pp_proxy_tensors: Optional[PPProxyTensors] = None,
) -> torch.Tensor:
with get_attn_tp_context().maybe_input_scattered(forward_batch):
hidden_states = self.model(
input_ids, positions, forward_batch, input_embeds, pp_proxy_tensors
)
aux_hidden_states = None
if isinstance(hidden_states, tuple):
hidden_states, aux_hidden_states = hidden_states
return self.logits_processor(
input_ids,
hidden_states,
self.lm_head,
forward_batch,
aux_hidden_states,
)
def get_input_embeddings(self) -> nn.Embedding:
return self.model.embed_tokens
def get_embed_and_head(self):
return self.model.embed_tokens.weight, self.lm_head.weight
def set_embed(self, embed: torch.Tensor) -> None:
# If draft hidden size != target hidden size, embeddings can't be shared.
if (
hasattr(self.config, "target_hidden_size")
and self.config.target_hidden_size is not None
and self.config.target_hidden_size != self.config.hidden_size
):
return
del self.model.embed_tokens.weight
self.model.embed_tokens.weight = embed
torch.cuda.empty_cache()
torch.cuda.synchronize()
def set_embed_and_head(self, embed: torch.Tensor, head: torch.Tensor) -> None:
del self.model.embed_tokens.weight
del self.lm_head.weight
self.model.embed_tokens.weight = embed
self.lm_head.weight = head
torch.cuda.empty_cache()
torch.cuda.synchronize()
def get_hot_token_id(self):
return self.hot_token_id
def load_weights(self, weights: Iterable[Tuple[str, torch.Tensor]]) -> None:
params_dict = dict(self.named_parameters())
stacked_params_mapping = [
(".gate_up_proj", ".gate_proj", 0),
(".gate_up_proj", ".up_proj", 1),
]
cached_a_proj: dict[str, torch.Tensor] = {}
for name, loaded_weight in weights:
if name == "d2t" or name.endswith(".d2t"):
# d2t stores diffs between draft id and target id; absent in
# checkpoints whose draft_vocab_size equals vocab_size.
self.hot_token_id = loaded_weight + torch.arange(loaded_weight.shape[0])
continue
if name == "t2d" or name.endswith(".t2d"):
continue
# Map checkpoint layout (layers.0.X, embed_tokens.X, fc, norm) to the
# internal layout (model.midlayer.X, model.embed_tokens.X, model.fc,
# model.norm). lm_head stays at the top level.
mapped_name = re.sub(r"^layers\.0\.", "midlayer.", name)
if not mapped_name.startswith("lm_head."):
mapped_name = f"model.{mapped_name}"
handled = False
for param_name, weight_name, shard_id in stacked_params_mapping:
if weight_name not in mapped_name:
continue
target_name = mapped_name.replace(weight_name, param_name)
if target_name not in params_dict:
continue
param = params_dict[target_name]
weight_loader = getattr(param, "weight_loader", default_weight_loader)
weight_loader(param, loaded_weight, shard_id)
handled = True
break
if handled:
continue
if "q_a_proj" in mapped_name or "kv_a_proj_with_mqa" in mapped_name:
cached_a_proj[mapped_name] = loaded_weight
q_name = (
mapped_name
if "q_a_proj" in mapped_name
else mapped_name.replace("kv_a_proj_with_mqa", "q_a_proj")
)
kv_name = (
mapped_name
if "kv_a_proj_with_mqa" in mapped_name
else mapped_name.replace("q_a_proj", "kv_a_proj_with_mqa")
)
if q_name in cached_a_proj and kv_name in cached_a_proj:
fused_weight = torch.cat(
[cached_a_proj[q_name], cached_a_proj[kv_name]], dim=0
)
fused_name = q_name.replace("q_a_proj", "fused_qkv_a_proj_with_mqa")
if fused_name in params_dict:
param = params_dict[fused_name]
weight_loader = getattr(
param, "weight_loader", default_weight_loader
)
weight_loader(param, fused_weight)
cached_a_proj.pop(q_name)
cached_a_proj.pop(kv_name)
continue
if mapped_name not in params_dict:
logger.warning("Eagle3 MLA: skipping unexpected weight %s", name)
continue
param = params_dict[mapped_name]
weight_loader = getattr(param, "weight_loader", default_weight_loader)
weight_loader(param, loaded_weight)
self.post_load_weights()
def post_load_weights(self) -> None:
"""Split kv_b_proj into w_kc / w_vc tensors used by MLA absorb_core.
DeepseekV2 normally does this in DeepseekV2WeightLoaderMixin.post_load_weights;
we re-implement the bf16 fast-path directly here to keep the eagle3 draft
path independent of the full DeepseekV2 weight loader.
"""
self_attn = self.model.midlayer.self_attn
w = self_attn.kv_b_proj.weight
if w.dtype not in (torch.bfloat16, torch.float16, torch.float32):
raise NotImplementedError(
f"Eagle3 MLA draft post_load_weights only supports float dtypes, got {w.dtype}"
)
w_kc, w_vc = w.unflatten(
0, (-1, self_attn.qk_nope_head_dim + self_attn.v_head_dim)
).split([self_attn.qk_nope_head_dim, self_attn.v_head_dim], dim=1)
self_attn.w_kc = w_kc.transpose(1, 2).contiguous().transpose(1, 2)
self_attn.w_vc = w_vc.contiguous().transpose(1, 2)
EntryClass = [Eagle3DeepseekV2ForCausalLM]