[Model] Add support for JetBrains' Mellum v2 code generation model (#27375)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Jiminator <69131491+Jiminator@users.noreply.github.com> Co-authored-by: Alex Nails <alex.nails@radixark.ai>
This commit is contained in:
co-authored by
gemini-code-assist[bot]
Jiminator
Alex Nails
parent
7e229e2a81
commit
702bddcee8
@@ -1901,6 +1901,7 @@ def is_hybrid_swa_model(
|
||||
"Gemma4ForConditionalGeneration",
|
||||
"Gemma4UnifiedForConditionalGeneration",
|
||||
"LagunaForCausalLM",
|
||||
"MellumForCausalLM",
|
||||
"UnlimitedOCRForCausalLM",
|
||||
}
|
||||
if any(arch in hybrid_swa_archs for arch in model_architectures):
|
||||
@@ -1972,6 +1973,8 @@ def get_hybrid_layer_ids(
|
||||
"Gemma4ForCausalLM" in model_architectures
|
||||
or "Gemma4ForConditionalGeneration" in model_architectures
|
||||
or "Gemma4UnifiedForConditionalGeneration" in model_architectures
|
||||
or "LagunaForCausalLM" in model_architectures
|
||||
or "MellumForCausalLM" in model_architectures
|
||||
):
|
||||
layer_types = getattr(hf_text_config, "layer_types", [])
|
||||
swa_attention_layer_ids = [
|
||||
@@ -1980,14 +1983,6 @@ def get_hybrid_layer_ids(
|
||||
full_attention_layer_ids = [
|
||||
i for i, x in enumerate(layer_types) if x == "full_attention"
|
||||
]
|
||||
elif "LagunaForCausalLM" in model_architectures:
|
||||
layer_types = getattr(hf_text_config, "layer_types", [])
|
||||
swa_attention_layer_ids = [
|
||||
i for i, x in enumerate(layer_types) if x == "sliding_attention"
|
||||
]
|
||||
full_attention_layer_ids = [
|
||||
i for i, x in enumerate(layer_types) if x == "full_attention"
|
||||
]
|
||||
elif "UnlimitedOCRForCausalLM" in model_architectures:
|
||||
swa_attention_layer_ids = list(range(num_hidden_layers))
|
||||
full_attention_layer_ids = []
|
||||
|
||||
@@ -0,0 +1,595 @@
|
||||
# Copyright 2023-2026 SGLang Team
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
|
||||
"""
|
||||
Inference-only Mellum (JetBrains/Mellum2-12B-A2.5B family) Qwen3-MoE variant with
|
||||
interleaved sliding-window/full attention, per-layer-type RoPE and mixed dense/MoE
|
||||
MLP layers.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Any, Dict, Optional, Tuple, cast
|
||||
|
||||
import torch
|
||||
from torch import nn
|
||||
from transformers import PretrainedConfig
|
||||
|
||||
from sglang.srt.distributed import get_pp_group
|
||||
from sglang.srt.layers.communicator import LayerCommunicator, LayerScatterModes
|
||||
from sglang.srt.layers.layernorm import RMSNorm
|
||||
from sglang.srt.layers.linear import QKVParallelLinear, RowParallelLinear
|
||||
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
||||
from sglang.srt.layers.radix_attention import RadixAttention
|
||||
from sglang.srt.layers.rotary_embedding import MRotaryEmbedding, get_rope
|
||||
from sglang.srt.layers.rotary_embedding.yarn import (
|
||||
yarn_find_correction_range,
|
||||
yarn_get_mscale,
|
||||
)
|
||||
from sglang.srt.layers.vocab_parallel_embedding import ParallelLMHead
|
||||
from sglang.srt.model_executor.forward_batch_info import ForwardBatch, PPProxyTensors
|
||||
from sglang.srt.models.qwen3_moe import (
|
||||
Qwen3MoeAttention,
|
||||
Qwen3MoeDecoderLayer,
|
||||
Qwen3MoeForCausalLM,
|
||||
Qwen3MoeMLP,
|
||||
Qwen3MoeModel,
|
||||
Qwen3MoeSparseMoeBlock,
|
||||
)
|
||||
from sglang.srt.models.utils import (
|
||||
apply_qk_norm,
|
||||
create_fused_set_kv_buffer_arg,
|
||||
enable_fused_set_kv_buffer,
|
||||
)
|
||||
from sglang.srt.runtime_context import get_parallel, get_server_args
|
||||
from sglang.srt.utils import add_prefix, is_cuda
|
||||
|
||||
_is_cuda = is_cuda()
|
||||
|
||||
if _is_cuda:
|
||||
from sglang.jit_kernel.fused_qknorm_rope import (
|
||||
can_use_fused_qk_norm_rope,
|
||||
fused_qk_norm_rope,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_rope_type(rope_params: Dict[str, Any]) -> str:
|
||||
return rope_params.get("rope_type") or rope_params.get("type") or "default"
|
||||
|
||||
|
||||
def _compute_yarn_from_rope_params(
|
||||
rope_params: Dict[str, Any],
|
||||
head_dim: int,
|
||||
max_position_embeddings: int,
|
||||
) -> Dict[str, float]:
|
||||
_default = {"factor": 1.0, "low": 0, "high": 0, "attention_factor": 1.0}
|
||||
if rope_params is None:
|
||||
return _default
|
||||
|
||||
if _get_rope_type(rope_params) == "default":
|
||||
return _default
|
||||
|
||||
base = rope_params.get("rope_theta", 10000)
|
||||
dim = head_dim
|
||||
factor = rope_params.get("factor", 1.0)
|
||||
attention_factor = rope_params.get("attention_factor")
|
||||
mscale = rope_params.get("mscale")
|
||||
mscale_all_dim = rope_params.get("mscale_all_dim")
|
||||
|
||||
original_max_position_embeddings = rope_params.get(
|
||||
"original_max_position_embeddings", max_position_embeddings
|
||||
)
|
||||
if "original_max_position_embeddings" in rope_params:
|
||||
factor = max_position_embeddings / original_max_position_embeddings
|
||||
|
||||
if attention_factor is None:
|
||||
if mscale and mscale_all_dim:
|
||||
attention_factor = float(
|
||||
yarn_get_mscale(factor, mscale)
|
||||
/ yarn_get_mscale(factor, mscale_all_dim)
|
||||
)
|
||||
else:
|
||||
attention_factor = yarn_get_mscale(factor)
|
||||
|
||||
beta_fast = rope_params.get("beta_fast") or 32
|
||||
beta_slow = rope_params.get("beta_slow") or 1
|
||||
|
||||
truncate = rope_params.get("truncate", True)
|
||||
low, high = yarn_find_correction_range(
|
||||
beta_fast, beta_slow, dim, base, original_max_position_embeddings, truncate
|
||||
)
|
||||
return {
|
||||
"factor": factor,
|
||||
"low": low,
|
||||
"high": high,
|
||||
"attention_factor": attention_factor,
|
||||
}
|
||||
|
||||
|
||||
def get_attention_sliding_window_size(config: PretrainedConfig) -> Optional[int]:
|
||||
sw = getattr(config, "sliding_window", None)
|
||||
if sw is not None:
|
||||
return sw - 1
|
||||
return None
|
||||
|
||||
|
||||
class MellumMLP(Qwen3MoeMLP):
|
||||
# Qwen3MoeDecoderLayer.forward calls self.mlp(x, forward_batch).
|
||||
def forward(self, x, forward_batch=None):
|
||||
return super().forward(x)
|
||||
|
||||
|
||||
class MellumAttention(Qwen3MoeAttention):
|
||||
"""
|
||||
Qwen3MoeAttention with per-layer sliding window and per-layer-type RoPE.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hidden_size: int,
|
||||
num_heads: int,
|
||||
num_kv_heads: int,
|
||||
layer_id: int = 0,
|
||||
start_layer: int = 0,
|
||||
rope_params: Optional[Dict[str, Any]] = None,
|
||||
max_position_embeddings: int = 8192,
|
||||
head_dim: Optional[int] = None,
|
||||
rms_norm_eps: float = 1e-06,
|
||||
attention_bias: bool = False,
|
||||
config: Optional[PretrainedConfig] = None,
|
||||
quant_config: Optional[QuantizationConfig] = None,
|
||||
prefix: str = "",
|
||||
sliding_window_size: int = -1,
|
||||
alt_stream: Optional[torch.cuda.Stream] = None,
|
||||
) -> None:
|
||||
# Skip Qwen3MoeAttention.__init__ so that we can pass the correct
|
||||
# correct per-layer rope_theta / rope_scaling and sliding window.
|
||||
nn.Module.__init__(self)
|
||||
|
||||
self.hidden_size = hidden_size
|
||||
self.start_layer = start_layer
|
||||
|
||||
attn_tp_rank = get_parallel().attn_tp_rank
|
||||
attn_tp_size = get_parallel().attn_tp_size
|
||||
|
||||
self.config = config
|
||||
self.total_num_heads = num_heads
|
||||
assert self.total_num_heads % attn_tp_size == 0
|
||||
self.num_heads = self.total_num_heads // attn_tp_size
|
||||
self.total_num_kv_heads = num_kv_heads
|
||||
if self.total_num_kv_heads >= attn_tp_size:
|
||||
assert self.total_num_kv_heads % attn_tp_size == 0
|
||||
else:
|
||||
assert attn_tp_size % self.total_num_kv_heads == 0
|
||||
self.num_kv_heads = max(1, self.total_num_kv_heads // attn_tp_size)
|
||||
self.head_dim = head_dim or hidden_size // self.total_num_heads
|
||||
self.q_size = self.num_heads * self.head_dim
|
||||
self.kv_size = self.num_kv_heads * self.head_dim
|
||||
self.scaling = self.head_dim**-0.5
|
||||
self.max_position_embeddings = max_position_embeddings
|
||||
|
||||
rope_params = rope_params or {}
|
||||
self.rope_theta = rope_params.get("rope_theta", 10000.0)
|
||||
rope_scaling = rope_params if _get_rope_type(rope_params) != "default" else None
|
||||
|
||||
self.tp_rank = get_parallel().tp_rank
|
||||
|
||||
self.qkv_proj = QKVParallelLinear(
|
||||
hidden_size,
|
||||
self.head_dim,
|
||||
self.total_num_heads,
|
||||
self.total_num_kv_heads,
|
||||
bias=attention_bias,
|
||||
quant_config=quant_config,
|
||||
tp_rank=attn_tp_rank,
|
||||
tp_size=attn_tp_size,
|
||||
prefix=add_prefix("qkv_proj", prefix),
|
||||
)
|
||||
|
||||
self.o_proj = RowParallelLinear(
|
||||
self.total_num_heads * self.head_dim,
|
||||
hidden_size,
|
||||
bias=attention_bias,
|
||||
quant_config=quant_config,
|
||||
tp_rank=attn_tp_rank,
|
||||
tp_size=attn_tp_size,
|
||||
reduce_results=False,
|
||||
prefix=add_prefix("o_proj", prefix),
|
||||
)
|
||||
|
||||
self.rotary_emb = get_rope(
|
||||
self.head_dim,
|
||||
rotary_dim=self.head_dim,
|
||||
max_position=max_position_embeddings,
|
||||
base=self.rope_theta,
|
||||
rope_scaling=rope_scaling,
|
||||
)
|
||||
self.compatible_with_fused_kv_buffer = not isinstance(
|
||||
self.rotary_emb, MRotaryEmbedding
|
||||
)
|
||||
self.compatible_with_fused_qk_norm_rope = not isinstance(
|
||||
self.rotary_emb, MRotaryEmbedding
|
||||
) and self.head_dim in (64, 128, 256)
|
||||
|
||||
# TODO: Precompute YaRN parameters in Qwen3MoeAttention for all
|
||||
# inheriting models instead of recomputing them in every forward pass.
|
||||
self._yarn_params = _compute_yarn_from_rope_params(
|
||||
rope_params, self.head_dim, max_position_embeddings
|
||||
)
|
||||
_yarn_factor = self._yarn_params["factor"]
|
||||
|
||||
self.use_fused_qk_norm_rope = (
|
||||
get_server_args().enable_fused_qk_norm_rope
|
||||
and self.compatible_with_fused_qk_norm_rope
|
||||
and _is_cuda
|
||||
and can_use_fused_qk_norm_rope(
|
||||
self.head_dim,
|
||||
self.rotary_emb.is_neox_style,
|
||||
torch.bfloat16,
|
||||
_yarn_factor != 1.0,
|
||||
)
|
||||
)
|
||||
self._used_fused_qk_norm_rope_last_call = False
|
||||
|
||||
self.attn = RadixAttention(
|
||||
self.num_heads,
|
||||
self.head_dim,
|
||||
self.scaling,
|
||||
num_kv_heads=self.num_kv_heads,
|
||||
layer_id=layer_id,
|
||||
sliding_window_size=sliding_window_size,
|
||||
prefix=add_prefix("attn", prefix),
|
||||
)
|
||||
|
||||
self.q_norm = RMSNorm(self.head_dim, eps=rms_norm_eps)
|
||||
self.k_norm = RMSNorm(self.head_dim, eps=rms_norm_eps)
|
||||
self.alt_stream = alt_stream
|
||||
|
||||
def forward_prepare_npu(self, positions, hidden_states, forward_batch):
|
||||
raise NotImplementedError(
|
||||
"Mellum per-layer RoPE is incompatible with the shared rotary "
|
||||
"cos/sin priming in Qwen3MoeAttention.forward_prepare_npu"
|
||||
)
|
||||
|
||||
def apply_qk_norm_rope(self, qkv, positions, forward_batch):
|
||||
# Overridden to use pre-computed per-layer YaRN params.
|
||||
use_fused = self.use_fused_qk_norm_rope and qkv.dtype == torch.bfloat16
|
||||
if use_fused:
|
||||
theta = self.rope_theta
|
||||
fused_qk_norm_rope(
|
||||
qkv,
|
||||
self.num_heads,
|
||||
self.num_kv_heads,
|
||||
self.num_kv_heads,
|
||||
self.head_dim,
|
||||
self.q_norm.variance_epsilon,
|
||||
self.q_norm.weight,
|
||||
self.k_norm.weight,
|
||||
theta,
|
||||
self.rotary_emb.is_neox_style,
|
||||
positions,
|
||||
self._yarn_params["factor"],
|
||||
self._yarn_params["low"],
|
||||
self._yarn_params["high"],
|
||||
self._yarn_params["attention_factor"],
|
||||
)
|
||||
q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
|
||||
self._used_fused_qk_norm_rope_last_call = True
|
||||
else:
|
||||
q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
|
||||
q, k = apply_qk_norm(
|
||||
q=q,
|
||||
k=k,
|
||||
q_norm=self.q_norm,
|
||||
k_norm=self.k_norm,
|
||||
head_dim=self.head_dim,
|
||||
alt_stream=self.alt_stream,
|
||||
)
|
||||
q, k = self.rotary_emb(
|
||||
positions,
|
||||
q,
|
||||
k,
|
||||
fused_set_kv_buffer_arg=(
|
||||
create_fused_set_kv_buffer_arg(
|
||||
value=v,
|
||||
layer=self.attn,
|
||||
forward_batch=forward_batch,
|
||||
)
|
||||
if enable_fused_set_kv_buffer(forward_batch)
|
||||
and self.compatible_with_fused_kv_buffer
|
||||
else None
|
||||
),
|
||||
)
|
||||
self._used_fused_qk_norm_rope_last_call = False
|
||||
return q, k, v
|
||||
|
||||
|
||||
class MellumDecoderLayer(Qwen3MoeDecoderLayer):
|
||||
"""
|
||||
Qwen3MoeDecoderLayer with per-layer attention type, RoPE and dense/sparse MLP.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: PretrainedConfig,
|
||||
layer_id: int,
|
||||
start_layer: int = 0,
|
||||
quant_config: Optional[QuantizationConfig] = None,
|
||||
prefix: str = "",
|
||||
alt_stream: Optional[torch.cuda.Stream] = None,
|
||||
) -> None:
|
||||
# As with MellumAttention, skip parent __init__ to wire up
|
||||
# per-layer attention params, RoPE params, sliding window
|
||||
# and mixed dense/MoE MLP.
|
||||
nn.Module.__init__(self)
|
||||
|
||||
self.config = config
|
||||
cfg = cast(Any, config)
|
||||
self.hidden_size = cfg.hidden_size
|
||||
self.layer_id = layer_id
|
||||
|
||||
layer_types = cfg.layer_types
|
||||
if len(layer_types) != cfg.num_hidden_layers:
|
||||
raise ValueError(
|
||||
"Expected len(layer_types) == num_hidden_layers, got "
|
||||
f"{len(layer_types)} and {cfg.num_hidden_layers}"
|
||||
)
|
||||
layer_type = layer_types[layer_id]
|
||||
if layer_type not in ("sliding_attention", "full_attention"):
|
||||
raise ValueError(
|
||||
f"Unsupported layer_types[{layer_id}]={layer_type}; "
|
||||
"expected 'sliding_attention' or 'full_attention'"
|
||||
)
|
||||
|
||||
rope_parameters = cfg.rope_parameters
|
||||
rope_params = rope_parameters.get(layer_type)
|
||||
if rope_params is None:
|
||||
raise ValueError(
|
||||
f"Missing rope_parameters[{layer_type}] for Mellum layer {layer_id}"
|
||||
)
|
||||
|
||||
# Mellum routes SWA per-layer via layer_types. Preserve the configured
|
||||
# window regardless of legacy use_sliding_window post-init side effects.
|
||||
if layer_type == "sliding_attention":
|
||||
sliding_window_size = get_attention_sliding_window_size(config)
|
||||
if sliding_window_size is None:
|
||||
raise ValueError(
|
||||
"Missing config.sliding_window for Mellum "
|
||||
f"sliding_attention layer {layer_id}"
|
||||
)
|
||||
else:
|
||||
sliding_window_size = -1
|
||||
|
||||
max_position_embeddings = cfg.max_position_embeddings
|
||||
head_dim = cfg.head_dim
|
||||
rms_norm_eps = cfg.rms_norm_eps
|
||||
attention_bias = cfg.attention_bias
|
||||
|
||||
self.self_attn = MellumAttention(
|
||||
hidden_size=self.hidden_size,
|
||||
num_heads=cfg.num_attention_heads,
|
||||
num_kv_heads=cfg.num_key_value_heads,
|
||||
layer_id=layer_id,
|
||||
start_layer=start_layer,
|
||||
rope_params=rope_params,
|
||||
max_position_embeddings=max_position_embeddings,
|
||||
head_dim=head_dim,
|
||||
rms_norm_eps=rms_norm_eps,
|
||||
attention_bias=attention_bias,
|
||||
config=config,
|
||||
quant_config=quant_config,
|
||||
prefix=add_prefix("self_attn", prefix),
|
||||
sliding_window_size=sliding_window_size,
|
||||
alt_stream=alt_stream,
|
||||
)
|
||||
|
||||
self.attn_tp_size = get_parallel().attn_tp_size
|
||||
self.attn_tp_rank = get_parallel().attn_tp_rank
|
||||
|
||||
mlp_layer_types = cfg.mlp_layer_types
|
||||
num_experts = cfg.num_experts
|
||||
|
||||
if len(mlp_layer_types) != cfg.num_hidden_layers:
|
||||
raise ValueError(
|
||||
"Expected len(mlp_layer_types) == num_hidden_layers, got "
|
||||
f"{len(mlp_layer_types)} and {cfg.num_hidden_layers}"
|
||||
)
|
||||
|
||||
def _is_sparse(lid: int) -> bool:
|
||||
if lid < 0 or lid >= cfg.num_hidden_layers:
|
||||
return False
|
||||
mlp_type = mlp_layer_types[lid]
|
||||
if mlp_type not in ("sparse", "dense"):
|
||||
raise ValueError(
|
||||
f"Unsupported mlp_layer_types[{lid}]={mlp_type}; "
|
||||
"expected 'sparse' or 'dense'"
|
||||
)
|
||||
return mlp_type == "sparse"
|
||||
|
||||
self.is_layer_sparse = _is_sparse(layer_id)
|
||||
|
||||
if self.is_layer_sparse:
|
||||
if num_experts <= 0:
|
||||
raise ValueError(
|
||||
"Sparse MLP requested but num_experts <= 0 in Mellum config"
|
||||
)
|
||||
self.mlp = Qwen3MoeSparseMoeBlock(
|
||||
layer_id=layer_id,
|
||||
config=cfg,
|
||||
quant_config=quant_config,
|
||||
prefix=add_prefix("mlp", prefix),
|
||||
)
|
||||
else:
|
||||
self.mlp = MellumMLP(
|
||||
hidden_size=cfg.hidden_size,
|
||||
intermediate_size=cfg.intermediate_size,
|
||||
hidden_act=cfg.hidden_act,
|
||||
quant_config=quant_config,
|
||||
prefix=add_prefix("mlp", prefix),
|
||||
)
|
||||
|
||||
is_previous_layer_sparse = _is_sparse(layer_id - 1)
|
||||
is_next_layer_sparse = _is_sparse(layer_id + 1)
|
||||
|
||||
self.layer_scatter_modes = LayerScatterModes.init_new(
|
||||
layer_id=layer_id,
|
||||
num_layers=cfg.num_hidden_layers,
|
||||
is_layer_sparse=self.is_layer_sparse,
|
||||
is_previous_layer_sparse=is_previous_layer_sparse,
|
||||
is_next_layer_sparse=is_next_layer_sparse,
|
||||
)
|
||||
|
||||
self.input_layernorm = RMSNorm(cfg.hidden_size, eps=rms_norm_eps)
|
||||
self.post_attention_layernorm = RMSNorm(cfg.hidden_size, eps=rms_norm_eps)
|
||||
|
||||
self.layer_communicator = LayerCommunicator(
|
||||
layer_scatter_modes=self.layer_scatter_modes,
|
||||
input_layernorm=self.input_layernorm,
|
||||
post_attention_layernorm=self.post_attention_layernorm,
|
||||
allow_reduce_scatter=True,
|
||||
is_last_layer=(layer_id == cfg.num_hidden_layers - 1),
|
||||
)
|
||||
|
||||
|
||||
class MellumModel(Qwen3MoeModel):
|
||||
def __init__(
|
||||
self,
|
||||
config: PretrainedConfig,
|
||||
quant_config: Optional[QuantizationConfig] = None,
|
||||
prefix: str = "",
|
||||
) -> None:
|
||||
super().__init__(
|
||||
config=config,
|
||||
quant_config=quant_config,
|
||||
prefix=prefix,
|
||||
decoder_layer_type=MellumDecoderLayer,
|
||||
)
|
||||
|
||||
|
||||
class MellumForCausalLM(Qwen3MoeForCausalLM):
|
||||
packed_modules_mapping = {
|
||||
"qkv_proj": ["q_proj", "k_proj", "v_proj"],
|
||||
"gate_up_proj": ["gate_proj", "up_proj"],
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: PretrainedConfig,
|
||||
quant_config: Optional[QuantizationConfig] = None,
|
||||
prefix: str = "",
|
||||
) -> None:
|
||||
nn.Module.__init__(self)
|
||||
|
||||
from sglang.srt.layers.logits_processor import LogitsProcessor
|
||||
|
||||
self.pp_group = get_pp_group()
|
||||
cfg = cast(Any, config)
|
||||
self.config = cfg
|
||||
self.quant_config = quant_config
|
||||
|
||||
if hasattr(cfg, "max_window_layers"):
|
||||
logger.warning(
|
||||
"Mellum ignores config.max_window_layers and relies on "
|
||||
"config.layer_types for per-layer SWA routing."
|
||||
)
|
||||
|
||||
self.model = MellumModel(cfg, quant_config, prefix=add_prefix("model", prefix))
|
||||
# Over-approximation is safe: the non-fused path accepts int32 positions.
|
||||
self.use_fused_qk_norm_rope = any(
|
||||
getattr(
|
||||
getattr(layer, "self_attn", None),
|
||||
"use_fused_qk_norm_rope",
|
||||
False,
|
||||
)
|
||||
for layer in self.model.layers
|
||||
)
|
||||
self.lm_head = ParallelLMHead(
|
||||
cfg.vocab_size,
|
||||
cfg.hidden_size,
|
||||
quant_config=quant_config,
|
||||
prefix=add_prefix("lm_head", prefix),
|
||||
use_attn_tp_group=get_server_args().enable_dp_lm_head,
|
||||
)
|
||||
self.logits_processor = LogitsProcessor(cfg)
|
||||
self.capture_aux_hidden_states = False
|
||||
|
||||
self.attn_cp_size = get_parallel().attn_cp_size
|
||||
self.attn_cp_rank = get_parallel().attn_cp_rank
|
||||
self.moe_dp_size = get_parallel().moe_dp_size
|
||||
|
||||
assert self.attn_cp_size % self.moe_dp_size == 0, (
|
||||
f"attn_cp_size ({self.attn_cp_size}) must be divisible by "
|
||||
f"moe_dp_size ({self.moe_dp_size})"
|
||||
)
|
||||
|
||||
def _prepare_positions(
|
||||
self,
|
||||
positions: torch.Tensor,
|
||||
device: torch.device,
|
||||
) -> torch.Tensor:
|
||||
if self.use_fused_qk_norm_rope:
|
||||
return positions.view(-1).to(dtype=torch.int32, device=device).contiguous()
|
||||
return positions
|
||||
|
||||
@torch.no_grad()
|
||||
def forward(
|
||||
self,
|
||||
input_ids: torch.Tensor,
|
||||
positions: torch.Tensor,
|
||||
forward_batch: ForwardBatch,
|
||||
input_embeds: Optional[torch.Tensor] = None,
|
||||
pp_proxy_tensors: Optional[PPProxyTensors] = None,
|
||||
) -> torch.Tensor:
|
||||
if pp_proxy_tensors is not None:
|
||||
positions_device = pp_proxy_tensors["hidden_states"].device
|
||||
elif input_embeds is not None:
|
||||
positions_device = input_embeds.device
|
||||
else:
|
||||
positions_device = input_ids.device
|
||||
positions = self._prepare_positions(positions, positions_device)
|
||||
return super().forward(
|
||||
input_ids,
|
||||
positions,
|
||||
forward_batch,
|
||||
input_embeds,
|
||||
pp_proxy_tensors=pp_proxy_tensors,
|
||||
)
|
||||
|
||||
@torch.no_grad()
|
||||
def forward_split_prefill(
|
||||
self,
|
||||
input_ids: torch.Tensor,
|
||||
positions: torch.Tensor,
|
||||
forward_batch: ForwardBatch,
|
||||
split_interval: Tuple[int, int],
|
||||
input_embeds: Optional[torch.Tensor] = None,
|
||||
):
|
||||
positions_device = (
|
||||
input_embeds.device if input_embeds is not None else input_ids.device
|
||||
)
|
||||
positions = self._prepare_positions(positions, positions_device)
|
||||
return super().forward_split_prefill(
|
||||
input_ids,
|
||||
positions,
|
||||
forward_batch,
|
||||
split_interval,
|
||||
input_embeds,
|
||||
)
|
||||
|
||||
def get_attention_sliding_window_size(self) -> Optional[int]:
|
||||
return get_attention_sliding_window_size(self.config)
|
||||
|
||||
|
||||
EntryClass = MellumForCausalLM
|
||||
@@ -142,6 +142,36 @@ try:
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# Newer transformers versions (>=5.10.2) expose MellumConfig directly,
|
||||
# but fallback to Qwen3MoeConfig for older versions.
|
||||
try:
|
||||
import transformers as _hf_transformers
|
||||
|
||||
_HFMellumConfig = getattr(_hf_transformers, "MellumConfig", None)
|
||||
|
||||
if _HFMellumConfig is not None:
|
||||
_CONFIG_REGISTRY["mellum"] = _HFMellumConfig
|
||||
else:
|
||||
from transformers import Qwen3MoeConfig as _HFQwen3MoeConfig
|
||||
|
||||
class _MellumConfigAlias(_HFQwen3MoeConfig):
|
||||
model_type = "mellum"
|
||||
|
||||
def __post_init__(self, **kwargs):
|
||||
# Qwen3MoeConfig.__post_init__ wipes sliding_window unless
|
||||
# use_sliding_window=True. Mellum gates sliding attention
|
||||
# per-layer via layer_types, so preserve sliding_window
|
||||
# regardless of the legacy use_sliding_window flag.
|
||||
sliding_window = getattr(self, "sliding_window", None)
|
||||
super().__post_init__(**kwargs)
|
||||
self.sliding_window = sliding_window
|
||||
|
||||
_CONFIG_REGISTRY["mellum"] = _MellumConfigAlias
|
||||
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
from transformers import Gemma4Config as _HFGemma4Config
|
||||
|
||||
|
||||
Reference in New Issue
Block a user