Skip to content

vllm.model_executor.models.muse_glimmer

Inference-only MuseGlimmer multimodal model for vLLM.

Native port of the MuseGlimmer text decoder (MuseGlimmerForCausalLM). The text stack is a Gemma2 derivative with the following MuseGlimmer-specific deltas, each of which is handled explicitly here:

  • SiLU-gated MLP (hidden_activation="silu"), not Gemma's gelu-tanh.
  • Scaleless RMSNorm on the token embeddings (no sqrt(hidden) scaling).
  • Per-layer sandwich RMSNorms with a baked +1 weight offset (x * (1 + w)), matching Gemma, but with distinct eps for the pre/post norms (rms_norm_eps vs post_norm_eps).
  • QK-norm (weightless, fp32) applied before RoPE, followed by a query pre-scale of qk_scale_factor / sqrt(head_dim).
  • A per-head sigmoid attention output gate.
  • iRoPE layout: NoPE layers use full attention, RoPE layers use sliding window attention. RoPE is applied NEOX-style (is_neox_style=True): the HF converter (convert_muse_glimmer_weights_to_hf.py, 20260806+) permutes q/k into the half-split (NEOX) layout via _permute_for_rope so they pair with rotate_half — matching the reference's interleaved rotation on the native (unpermuted) weights. Serving the permuted HF weights with is_neox_style=False scrambles q/k and causes token-repetition collapse.
  • Final logits are pre-scaled by output_multiplier and then tanh soft-capped at final_logit_softcapping.
  • Untied lm_head.

The vision path supports variable-resolution images and temporally patched videos. It mirrors the checkpoint's native vision encoder, including sparse block attention, 2-D RoPE, pixel-shuffle downsampling, and the two-layer adapter/projection stack.

Classes:

MuseGlimmerForCausalLM

Bases: Module, SupportsLoRA, SupportsMultiModal, SupportsPP, SupportsEagle3

Methods:

Source code in vllm/model_executor/models/muse_glimmer.py
@MULTIMODAL_REGISTRY.register_processor(
    MuseGlimmerMultiModalProcessor,
    info=MuseGlimmerProcessingInfo,
    dummy_inputs=MuseGlimmerDummyInputsBuilder,
)
class MuseGlimmerForCausalLM(
    nn.Module, SupportsLoRA, SupportsMultiModal, SupportsPP, SupportsEagle3
):
    # Weight-name normalization. Two checkpoint conventions are supported:
    #
    #   * HF MuseGlimmer export (``convert_muse_glimmer_weights_to_hf.py``): the
    #     multimodal ``MuseGlimmerConfig`` prefixes the language model with
    #     ``model.language_model.``; the per-layer sandwich norms are already
    #     named ``input_layernorm`` / ``post_attention_layernorm`` /
    #     ``pre_feedforward_layernorm`` / ``post_feedforward_layernorm``.
    #
    #   * Legacy HF export (an earlier checkpoint convention): uses ``model.``
    #     and a different sandwich-norm naming where ``post_attn_norm`` is the
    #     true post-attention norm and ``post_attention_layernorm`` is actually
    #     the pre-feedforward norm. We remap those to MuseGlimmer's names.
    #
    # CONVENTION DISAMBIGUATION (critical): the two checkpoint families use
    # DIFFERENT sandwich-norm names, and they must not be conflated:
    #
    #   * Canonical MuseGlimmer export (current
    #     ``convert_muse_glimmer_weights_to_hf.py`` — what partners ship):
    #     keys are ``model.language_model.layers.N.*`` and
    #     the norms are ALREADY named ``input_layernorm`` /
    #     ``post_attention_layernorm`` / ``pre_feedforward_layernorm`` /
    #     ``post_feedforward_layernorm``. No norm rename needed — pass through.
    #
    #   * Legacy HF export (an earlier checkpoint convention): keys are
    #     ``model.layers.N.*`` and the sandwich norms are
    #     named ``input_layernorm`` / ``post_attention_layernorm`` (this one is
    #     actually the PRE-feedforward norm) / ``post_attn_norm`` (the true
    #     post-attention norm) / ``post_ffn_norm``. These must be remapped.
    #
    # The unambiguous discriminator is the PREFIX: legacy keys start with
    # ``model.layers.`` while canonical keys start with
    # ``model.language_model.layers.``. ``orig_to_new_regex`` runs BEFORE the
    # prefix strip (see WeightsMapper._map_name_with_shard), so we anchor the
    # legacy renames on ``^model\.layers\.`` — they fire ONLY on legacy keys and
    # leave canonical/partner checkpoints untouched. Rule order within the regex
    # dict matters: the ``post_attention_layernorm`` -> ``pre_feedforward_...``
    # rule must precede the ``post_attn_norm`` -> ``post_attention_layernorm``
    # rule so the latter's output is not re-captured by the former (regex rules
    # apply as a single forward pass).
    hf_to_vllm_mapper = WeightsMapper(
        orig_to_new_substr={
            "model.vision_tower.patch_embedder.position_embedding_table.weight": (
                "model.vision_tower.positional_embedding_vlm"
            ),
            "model.vision_tower.layers.": "model.vision_tower.transformer.",
            ".norm1.": ".ln_1.",
            ".norm2.": ".ln_2.",
            ".attn.proj.": ".attn.o_proj.",
            ".mlp.fc1.": ".mlp.c_fc.",
            ".mlp.fc2.": ".mlp.c_proj.",
            ".self_attn.gate_proj": ".self_attn.output_gate_proj",
        },
        orig_to_new_prefix={
            "model.rotary_emb.": None,
            "model.language_model.": "model.",
            "language_model.": "model.",
            "model.vision_tower.patch_embedder.patch_embedding.": (
                "model.vision_tower.conv1_linear."
            ),
            "model.vision_tower.": "vision_encoder.",
            "vision_tower.": "vision_encoder.",
            "model.vision_encoder.": "vision_encoder.",
            "model.vision_adapter.fc1.": "model.vision_adapter.c_fc.",
            "model.vision_adapter.fc2.": "model.vision_adapter.c_proj.",
            "model.vision_adapter.": "vision_adapter.",
            "model.vision_projection.": "vision_projection.",
            "model.perception_emb_norm.": "perception_emb_norm.",
        },
        orig_to_new_stacked={
            ".q_proj": (".qkv_proj", "q"),
            ".k_proj": (".qkv_proj", "k"),
            ".v_proj": (".qkv_proj", "v"),
            ".gate_proj": (".gate_up_proj", 0),
            ".up_proj": (".gate_up_proj", 1),
        },
    )

    packed_modules_mapping = {
        "qkv_proj": ["q_proj", "k_proj", "v_proj"],
        "gate_up_proj": ["gate_proj", "up_proj"],
    }

    @classmethod
    def get_placeholder_str(cls, modality: str, i: int) -> str | None:
        if modality.startswith("image"):
            return IMAGE_TOKEN
        if modality.startswith("video"):
            return VIDEO_TOKEN
        raise ValueError(f"Unsupported MuseGlimmer modality: {modality}")

    def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
        super().__init__()
        config = vllm_config.model_config.hf_config
        text_config = _text_config(config)
        vision_config = _vision_config(config)
        quant_config = vllm_config.quant_config
        self.config = config
        self.text_config = text_config
        self.quant_config = quant_config
        self.has_vision = _muse_glimmer_has_vision(config)

        with self._mark_language_model(vllm_config):
            self.model = MuseGlimmerModel(
                vllm_config=vllm_config,
                prefix=maybe_prefix(prefix, "model"),
            )

        self.vision_encoder: MuseGlimmerVisionEncoder | None
        self.vision_adapter: MuseGlimmerVisionAdapter | None
        if self.has_vision:
            with self._mark_tower_model(vllm_config, {"image", "video"}):
                self.vision_encoder = MuseGlimmerVisionEncoder(
                    vision_config,
                    prefix=maybe_prefix(prefix, "vision_encoder"),
                )
                self.vision_adapter = MuseGlimmerVisionAdapter(vision_config)
                self.vision_projection = nn.Linear(
                    vision_config.adapter_dim,
                    text_config.hidden_size,
                    bias=False,
                )
                self.perception_emb_norm = (
                    MuseGlimmerRMSNorm(eps=text_config.rms_norm_eps, with_scale=False)
                    if text_config.normalize_tok_embeddings
                    else nn.Identity()
                )
        else:
            self.vision_encoder = None
            self.vision_adapter = None
            self.vision_projection = None
            self.perception_emb_norm = None

        self.lm_head = ParallelLMHead(
            text_config.vocab_size,
            text_config.hidden_size,
            quant_config=quant_config,
            prefix=maybe_prefix(prefix, "lm_head"),
        )
        self.output_multiplier = text_config.output_multiplier
        self.final_logit_softcapping = text_config.final_logit_softcapping
        self.logits_processor = LogitsProcessor(text_config.vocab_size)
        self.make_empty_intermediate_tensors = (
            self.model.make_empty_intermediate_tensors
        )
        image_token_id = getattr(
            config, "image_token_id", getattr(config, "patch_token_id", 200092)
        )
        video_token_id = getattr(config, "video_token_id", 200091)
        self.configure_mm_token_handling(
            text_config.vocab_size, [image_token_id, video_token_id]
        )

    def _parse_and_validate_image_input(
        self,
        **kwargs: object,
    ) -> MuseGlimmerImagePixelInputs | None:
        pixel_values = kwargs.pop("image_pixel_values", None)
        feature_sizes = kwargs.pop("image_feature_sizes", None)
        if pixel_values is None and feature_sizes is None:
            return None
        if pixel_values is None or feature_sizes is None:
            raise ValueError(
                "MuseGlimmer image_pixel_values and image_feature_sizes "
                "must be provided together"
            )
        if not isinstance(feature_sizes, torch.Tensor):
            raise ValueError("MuseGlimmer image_feature_sizes must be a tensor")
        if isinstance(pixel_values, torch.Tensor) and pixel_values.ndim == 3:
            pixel_values = pixel_values.unsqueeze(0)
        return MuseGlimmerImagePixelInputs(
            type="image_pixels",
            pixel_values=pixel_values,
            feature_sizes=feature_sizes.reshape(-1),
        )

    def _parse_and_validate_video_input(
        self,
        **kwargs: object,
    ) -> MuseGlimmerVideoPixelInputs | None:
        pixel_values = kwargs.pop("video_pixel_values", None)
        feature_sizes = kwargs.pop("video_feature_sizes", None)
        if pixel_values is None and feature_sizes is None:
            return None
        if pixel_values is None or feature_sizes is None:
            raise ValueError(
                "MuseGlimmer video_pixel_values and video_feature_sizes "
                "must be provided together"
            )
        if not isinstance(feature_sizes, torch.Tensor):
            raise ValueError("MuseGlimmer video_feature_sizes must be a tensor")
        if isinstance(pixel_values, torch.Tensor) and pixel_values.ndim == 4:
            pixel_values = pixel_values.unsqueeze(0)
        patch_temporal = int(_vision_config(self.config).patch_temporal)
        return MuseGlimmerVideoPixelInputs(
            type="video_pixels",
            pixel_values=pixel_values,
            feature_sizes=feature_sizes.reshape(-1),
            resolve_bindings={"c": patch_temporal * 3},
        )

    def _encode_pixel_groups(
        self,
        pixel_groups: Sequence[torch.Tensor],
        feature_sizes: Sequence[int],
    ) -> tuple[torch.Tensor, ...]:
        if (
            self.vision_encoder is None
            or self.vision_adapter is None
            or self.vision_projection is None
            or self.perception_emb_norm is None
        ):
            raise ValueError("This MuseGlimmer checkpoint has no vision tower")
        features = self.vision_encoder(pixel_groups)
        features = self.vision_adapter(features)
        features = self.vision_projection(features)
        features = self.perception_emb_norm(features)
        if features.shape[0] != sum(feature_sizes):
            raise ValueError(
                f"MuseGlimmer produced {features.shape[0]} vision features for "
                f"{sum(feature_sizes)} placeholder tokens"
            )
        return features.split(list(feature_sizes))

    def _process_image_input(
        self,
        image_input: MuseGlimmerImagePixelInputs,
    ) -> tuple[torch.Tensor, ...]:
        images = list(image_input["pixel_values"])
        sizes = [int(size) for size in image_input["feature_sizes"].tolist()]
        if len(images) != len(sizes):
            raise ValueError("MuseGlimmer image batch metadata does not match pixels")
        return self._encode_pixel_groups(images, sizes)

    def _process_video_input(
        self,
        video_input: MuseGlimmerVideoPixelInputs,
    ) -> tuple[torch.Tensor, ...]:
        videos = list(video_input["pixel_values"])
        sizes = [int(size) for size in video_input["feature_sizes"].tolist()]
        if len(videos) != len(sizes):
            raise ValueError("MuseGlimmer video batch metadata does not match pixels")
        groups = [group for video in videos for group in video]
        return self._encode_pixel_groups(groups, sizes)

    def embed_multimodal(self, **kwargs: object) -> MultiModalEmbeddings:
        image_input = self._parse_and_validate_image_input(**kwargs)
        video_input = self._parse_and_validate_video_input(**kwargs)

        embeddings: list[torch.Tensor] = []
        for key in kwargs:
            if key == "image_pixel_values" and image_input is not None:
                embeddings.extend(self._process_image_input(image_input))
            elif key == "video_pixel_values" and video_input is not None:
                embeddings.extend(self._process_video_input(video_input))
        return embeddings

    def forward(
        self,
        input_ids: torch.Tensor | None,
        positions: torch.Tensor,
        intermediate_tensors: IntermediateTensors | None = None,
        inputs_embeds: torch.Tensor | None = None,
        **kwargs: object,
    ) -> torch.Tensor | IntermediateTensors:
        return self.model(input_ids, positions, intermediate_tensors, inputs_embeds)

    def compute_logits(self, hidden_states: torch.Tensor) -> torch.Tensor | None:
        logits = self.logits_processor(self.lm_head, hidden_states)
        if logits is None:
            return None
        logits = logits * self.output_multiplier
        if self.final_logit_softcapping is not None:
            cap = self.final_logit_softcapping
            logits = cap * torch.tanh(logits / cap)
        return logits

    def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
        loader = AutoWeightsLoader(self)
        return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)

    def get_mm_mapping(self) -> MultiModelKeys:
        """
        Get the module prefix in multimodal models
        """
        return MultiModelKeys.from_string_field(
            language_model="model",
            connector=["vision_adapter.", "vision_projection."],
            tower_model="vision_encoder.",
        )

get_mm_mapping()

Get the module prefix in multimodal models

Source code in vllm/model_executor/models/muse_glimmer.py
def get_mm_mapping(self) -> MultiModelKeys:
    """
    Get the module prefix in multimodal models
    """
    return MultiModelKeys.from_string_field(
        language_model="model",
        connector=["vision_adapter.", "vision_projection."],
        tower_model="vision_encoder.",
    )

MuseGlimmerImagePixelInputs

Bases: TensorSchema

Batched variable-resolution image inputs.

Source code in vllm/model_executor/models/muse_glimmer.py
class MuseGlimmerImagePixelInputs(TensorSchema):
    """Batched variable-resolution image inputs."""

    type: Literal["image_pixels"]
    pixel_values: Annotated[
        torch.Tensor | list[torch.Tensor],
        TensorShape("bn", 3, "h", "w", dynamic_dims={"h", "w"}),
    ]
    feature_sizes: Annotated[torch.Tensor, TensorShape("bn")]

MuseGlimmerRMSNorm

Bases: Module

RMSNorm mirroring HF MuseGlimmer exactly (fp32 compute, cast at the end).

normed = _norm(x.float()) * (w.float() + weight_offset) cast back to the input dtype. When with_scale is False the layer is weightless (used for QK-norm and the token-embedding norm).

Source code in vllm/model_executor/models/muse_glimmer.py
class MuseGlimmerRMSNorm(nn.Module):
    """RMSNorm mirroring HF MuseGlimmer exactly (fp32 compute, cast at the end).

    ``normed = _norm(x.float()) * (w.float() + weight_offset)`` cast back to the
    input dtype. When ``with_scale`` is False the layer is weightless (used for
    QK-norm and the token-embedding norm).
    """

    def __init__(
        self,
        dim: int | None = None,
        eps: float = 1e-6,
        with_scale: bool = True,
        weight_offset: int = 0,
    ) -> None:
        super().__init__()
        self.eps = eps
        self.with_scale = with_scale
        self.weight_offset = weight_offset
        if with_scale:
            assert dim is not None
            self.weight = nn.Parameter(torch.zeros(dim))
        else:
            self.register_parameter("weight", None)

    def _norm(self, x: torch.Tensor) -> torch.Tensor:
        return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)

    def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
        out = self._norm(hidden_states.float())
        if self.with_scale:
            out = out * (self.weight.float() + self.weight_offset)
        return out.type_as(hidden_states)

MuseGlimmerVideoPixelInputs

Bases: TensorSchema

Batched variable-length, variable-resolution video inputs.

Source code in vllm/model_executor/models/muse_glimmer.py
class MuseGlimmerVideoPixelInputs(TensorSchema):
    """Batched variable-length, variable-resolution video inputs."""

    type: Literal["video_pixels"]
    pixel_values: Annotated[
        torch.Tensor | list[torch.Tensor],
        TensorShape(
            "bn",
            "ng",
            "c",
            "h",
            "w",
            dynamic_dims={"ng", "h", "w"},
        ),
    ]
    feature_sizes: Annotated[torch.Tensor, TensorShape("bn")]

_muse_glimmer_query_prescale(config)

Post-QK-norm query pre-scale (scale_query_by), normalized across the two config schemas so the net query scaling matches the native reference.

HF native modeling computes scale_query_by = qk_scale_factor / sqrt(head_dim) where the NATIVE qk_scale_factor is the raw params.json value (~43.784). The modular HF text_config PRE-FOLDS the 1/sqrt(head_dim) factor and ships qk_scale_factor = 43.784 / sqrt(128) = 3.87 already, expecting it applied directly. Both must yield the SAME scale_query_by (~3.87), then softmax uses scaling = head_dim**-0.5.

Precedence
  1. explicit scale_query_by (already the final factor) -> use as-is.
  2. else derive from qk_scale_factor:
  3. if it is already the folded value (~= qk_scale_factor/sqrt(hd) is NOT what we want; detect the native form and divide) — we decide by magnitude: the native raw value is folded * sqrt(head_dim). If qk_scale_factor is close to folded_expected * sqrt(hd) we treat it as native and divide; otherwise it is already folded, use directly.
Source code in vllm/model_executor/models/muse_glimmer.py
def _muse_glimmer_query_prescale(config) -> float:
    """Post-QK-norm query pre-scale (``scale_query_by``), normalized across the
    two config schemas so the net query scaling matches the native reference.

    HF native modeling computes ``scale_query_by = qk_scale_factor / sqrt(head_dim)``
    where the NATIVE ``qk_scale_factor`` is the raw ``params.json`` value
    (~43.784). The modular HF ``text_config`` PRE-FOLDS the ``1/sqrt(head_dim)``
    factor and ships ``qk_scale_factor = 43.784 / sqrt(128) = 3.87`` already,
    expecting it applied directly. Both must yield the SAME ``scale_query_by``
    (~3.87), then softmax uses ``scaling = head_dim**-0.5``.

    Precedence:
      1. explicit ``scale_query_by`` (already the final factor) -> use as-is.
      2. else derive from ``qk_scale_factor``:
         - if it is already the folded value (``~= qk_scale_factor/sqrt(hd)`` is
           NOT what we want; detect the native form and divide) — we decide by
           magnitude: the native raw value is ``folded * sqrt(head_dim)``. If
           ``qk_scale_factor`` is close to ``folded_expected * sqrt(hd)`` we treat
           it as native and divide; otherwise it is already folded, use directly.
    """
    head_dim = config.head_dim
    sqrt_hd = head_dim**0.5

    explicit = getattr(config, "scale_query_by", None)
    if explicit is not None:
        return float(explicit)

    qk_scale = getattr(config, "qk_scale_factor", None)
    if qk_scale is None:
        # No scale info at all: fall back to the plain 1/sqrt(head_dim) identity
        # (net query scaling then just the softmax scaling); should not happen
        # for real MuseGlimmer checkpoints, which always carry qk_scale_factor.
        return 1.0

    qk_scale = float(qk_scale)
    # Disambiguate native (raw, ~43.78) vs modular (folded, ~3.87). The native
    # form, when divided by sqrt(head_dim), yields the folded target; the folded
    # form is already the target. Native values are ~sqrt(head_dim)x larger than
    # folded. Use a threshold at sqrt(head_dim) (with margin): if qk_scale is
    # comparable to or larger than sqrt(head_dim), it is the native raw value and
    # must be divided; otherwise it is already folded and used directly.
    #   head_dim=128 -> sqrt=11.31; native 43.78 > 11.31 (divide -> 3.87),
    #   folded 3.87 < 11.31 (use as-is).
    if qk_scale >= sqrt_hd:
        return qk_scale / sqrt_hd
    return qk_scale

_muse_glimmer_use_attn_output_gate(config)

Whether the per-head sigmoid attention output gate is applied. MuseGlimmer ALWAYS applies it; the modular HF text_config omits use_attn_output_gate (reads as None). Missing/None -> True; only explicit False disables.

Source code in vllm/model_executor/models/muse_glimmer.py
def _muse_glimmer_use_attn_output_gate(config) -> bool:
    """Whether the per-head sigmoid attention output gate is applied. MuseGlimmer ALWAYS
    applies it; the modular HF ``text_config`` omits ``use_attn_output_gate``
    (reads as ``None``). Missing/None -> True; only explicit ``False`` disables."""
    val = getattr(config, "use_attn_output_gate", None)
    return True if val is None else bool(val)

_muse_glimmer_use_qk_norm(config)

Whether QK-norm is applied. MuseGlimmer ALWAYS applies QK-norm; the modular HF text_config schema simply omits use_qk_norm (reads as None). Treat a missing/None flag as True — only an explicit False disables it.

Source code in vllm/model_executor/models/muse_glimmer.py
def _muse_glimmer_use_qk_norm(config) -> bool:
    """Whether QK-norm is applied. MuseGlimmer ALWAYS applies QK-norm; the modular HF
    ``text_config`` schema simply omits ``use_qk_norm`` (reads as ``None``).
    Treat a missing/None flag as True — only an explicit ``False`` disables it."""
    val = getattr(config, "use_qk_norm", None)
    return True if val is None else bool(val)

_text_config(config)

MuseGlimmer checkpoints may nest the text config under text_config (multimodal MuseGlimmerConfig) or expose it directly (MuseGlimmerTextConfig).

Source code in vllm/model_executor/models/muse_glimmer.py
def _text_config(config):
    """MuseGlimmer checkpoints may nest the text config under ``text_config``
    (multimodal ``MuseGlimmerConfig``) or expose it directly
    (``MuseGlimmerTextConfig``)."""
    return getattr(config, "text_config", config)