remove vLLM plugin
Browse files- plugins/vllm_glm5v/glm5v.py +0 -241
plugins/vllm_glm5v/glm5v.py
DELETED
|
@@ -1,241 +0,0 @@
|
|
| 1 |
-
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
-
"""
|
| 3 |
-
glm5v: out-of-tree vLLM model = MoonViT (Kimi-K2.6) vision tower + trained MLP
|
| 4 |
-
projector + GLM-5.2 (GlmMoeDsa) text decoder.
|
| 5 |
-
|
| 6 |
-
This is a thin frankenstein over vLLM's in-tree `kimi_k25` VLM:
|
| 7 |
-
* vision tower + projector classes are imported VERBATIM from kimi_k25_vit
|
| 8 |
-
(the MoonViT3d encoder + KimiK25MultiModalProjector are bit-identical to the
|
| 9 |
-
encoder/projector we trained against).
|
| 10 |
-
* the text backbone is swapped from DeepseekV2ForCausalLM -> GlmMoeDsaForCausalLM
|
| 11 |
-
(GLM-5.2; same `deepseek_v2.py` MLA+MoE+DSA stack in vLLM).
|
| 12 |
-
* the image placeholder is GLM's `<|image|>` (154854), span
|
| 13 |
-
`<|begin_of_image|><|image|><|end_of_image|>`, instead of Kimi's `<|media_pad|>`.
|
| 14 |
-
* projector `linear_2` outputs GLM hidden (6144) via vision_config.mm_hidden_size.
|
| 15 |
-
|
| 16 |
-
Register out-of-tree via `ModelRegistry.register_model` (see register.py) with the
|
| 17 |
-
config.json `architectures = ["Glm5vForConditionalGeneration"]`.
|
| 18 |
-
"""
|
| 19 |
-
|
| 20 |
-
from collections.abc import Iterable, Mapping
|
| 21 |
-
from typing import Any
|
| 22 |
-
|
| 23 |
-
import torch
|
| 24 |
-
from torch import nn
|
| 25 |
-
|
| 26 |
-
from vllm.config import VllmConfig
|
| 27 |
-
from vllm.logger import init_logger
|
| 28 |
-
from vllm.model_executor.layers.quantization import QuantizationConfig
|
| 29 |
-
from vllm.model_executor.layers.quantization.compressed_tensors import (
|
| 30 |
-
compressed_tensors,
|
| 31 |
-
)
|
| 32 |
-
from vllm.model_executor.models.interfaces import (
|
| 33 |
-
SupportsMultiModal,
|
| 34 |
-
SupportsPP,
|
| 35 |
-
SupportsQuant,
|
| 36 |
-
)
|
| 37 |
-
from vllm.model_executor.models.kimi_k25 import (
|
| 38 |
-
KimiK25DummyInputsBuilder,
|
| 39 |
-
KimiK25MultiModalProcessor,
|
| 40 |
-
KimiK25ProcessingInfo,
|
| 41 |
-
)
|
| 42 |
-
from vllm.model_executor.models.kimi_k25_vit import (
|
| 43 |
-
KimiK25MultiModalProjector,
|
| 44 |
-
MoonViT3dPretrainedModel,
|
| 45 |
-
vision_tower_forward,
|
| 46 |
-
)
|
| 47 |
-
from vllm.multimodal import MULTIMODAL_REGISTRY
|
| 48 |
-
from vllm.multimodal.inputs import NestedTensors
|
| 49 |
-
from vllm.platforms import current_platform
|
| 50 |
-
from vllm.sequence import IntermediateTensors
|
| 51 |
-
|
| 52 |
-
from .glm5v_config import Glm5vConfig
|
| 53 |
-
from vllm.model_executor.models.utils import (
|
| 54 |
-
AutoWeightsLoader,
|
| 55 |
-
WeightsMapper,
|
| 56 |
-
init_vllm_registered_model,
|
| 57 |
-
maybe_prefix,
|
| 58 |
-
)
|
| 59 |
-
|
| 60 |
-
logger = init_logger(__name__)
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
class Glm5vProcessingInfo(KimiK25ProcessingInfo):
|
| 64 |
-
"""Processing info: GLM tokenizer + `<|image|>` placeholder + MoonViT image proc.
|
| 65 |
-
|
| 66 |
-
Reuses Kimi-K2.6's image processor (identical MoonViT preprocessing) and its
|
| 67 |
-
`KimiK25Processor` wiring; only the config type and placeholder token differ.
|
| 68 |
-
"""
|
| 69 |
-
|
| 70 |
-
def get_hf_config(self):
|
| 71 |
-
return self.ctx.get_hf_config(Glm5vConfig)
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
class Glm5vDummyInputsBuilder(KimiK25DummyInputsBuilder):
|
| 75 |
-
pass
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
class Glm5vMultiModalProcessor(KimiK25MultiModalProcessor):
|
| 79 |
-
pass
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
@MULTIMODAL_REGISTRY.register_processor(
|
| 83 |
-
Glm5vMultiModalProcessor,
|
| 84 |
-
info=Glm5vProcessingInfo,
|
| 85 |
-
dummy_inputs=Glm5vDummyInputsBuilder,
|
| 86 |
-
)
|
| 87 |
-
class Glm5vForConditionalGeneration(
|
| 88 |
-
nn.Module,
|
| 89 |
-
SupportsMultiModal,
|
| 90 |
-
SupportsPP,
|
| 91 |
-
SupportsQuant,
|
| 92 |
-
):
|
| 93 |
-
"""glm5v: MoonViT vision + trained projector + GLM-5.2 text."""
|
| 94 |
-
|
| 95 |
-
supports_encoder_tp_data = True
|
| 96 |
-
|
| 97 |
-
hf_to_vllm_mapper = WeightsMapper(
|
| 98 |
-
orig_to_new_prefix={
|
| 99 |
-
# GLM-5.2 text shards are stored with raw `model.*` / `lm_head.*` keys;
|
| 100 |
-
# nest them under the `language_model` submodule. (vision_tower.* and
|
| 101 |
-
# mm_projector.* keys are already correctly namespaced in our assembly.)
|
| 102 |
-
"lm_head.": "language_model.lm_head.",
|
| 103 |
-
"model.": "language_model.model.",
|
| 104 |
-
}
|
| 105 |
-
)
|
| 106 |
-
|
| 107 |
-
@classmethod
|
| 108 |
-
def get_placeholder_str(cls, modality: str, i: int) -> str | None:
|
| 109 |
-
if modality == "image":
|
| 110 |
-
# GLM image span; the inner <|image|> token is the one expanded.
|
| 111 |
-
return "<|begin_of_image|><|image|><|end_of_image|>"
|
| 112 |
-
raise ValueError(f"Unsupported modality: {modality}")
|
| 113 |
-
|
| 114 |
-
def __init__(self, vllm_config: VllmConfig, prefix: str = "") -> None:
|
| 115 |
-
super().__init__()
|
| 116 |
-
model_config = vllm_config.model_config
|
| 117 |
-
config: Glm5vConfig = model_config.hf_config
|
| 118 |
-
self.config = config
|
| 119 |
-
quant_config = vllm_config.quant_config
|
| 120 |
-
|
| 121 |
-
self.use_data_parallel = (
|
| 122 |
-
model_config.multimodal_config.mm_encoder_tp_mode == "data"
|
| 123 |
-
)
|
| 124 |
-
self.hidden_size = config.text_config.hidden_size
|
| 125 |
-
self.device = current_platform.current_device()
|
| 126 |
-
|
| 127 |
-
# Vision tower + projector (MoonViT, kept out of compressed-tensors quant).
|
| 128 |
-
with self._mark_tower_model(vllm_config, "vision_chunk"):
|
| 129 |
-
self.vision_tower = MoonViT3dPretrainedModel(
|
| 130 |
-
config.vision_config,
|
| 131 |
-
quant_config=self._maybe_ignore_quant_config(quant_config),
|
| 132 |
-
prefix=maybe_prefix(prefix, "vision_tower"),
|
| 133 |
-
)
|
| 134 |
-
self.vision_tower = self.vision_tower.to(
|
| 135 |
-
device=self.device, dtype=model_config.dtype
|
| 136 |
-
)
|
| 137 |
-
self.mm_projector = KimiK25MultiModalProjector(
|
| 138 |
-
config=config.vision_config,
|
| 139 |
-
use_data_parallel=self.use_data_parallel,
|
| 140 |
-
quant_config=self._maybe_ignore_quant_config(quant_config),
|
| 141 |
-
prefix=maybe_prefix(prefix, "mm_projector"),
|
| 142 |
-
)
|
| 143 |
-
self.mm_projector = self.mm_projector.to(
|
| 144 |
-
device=self.device, dtype=model_config.dtype
|
| 145 |
-
)
|
| 146 |
-
|
| 147 |
-
self.quant_config = quant_config
|
| 148 |
-
with self._mark_language_model(vllm_config):
|
| 149 |
-
self.language_model = init_vllm_registered_model(
|
| 150 |
-
vllm_config=vllm_config,
|
| 151 |
-
hf_config=config.text_config,
|
| 152 |
-
prefix=maybe_prefix(prefix, "language_model"),
|
| 153 |
-
architectures=["GlmMoeDsaForCausalLM"],
|
| 154 |
-
)
|
| 155 |
-
self.make_empty_intermediate_tensors = (
|
| 156 |
-
self.language_model.make_empty_intermediate_tensors
|
| 157 |
-
)
|
| 158 |
-
self.media_placeholder: int = self.config.media_placeholder_token_id
|
| 159 |
-
|
| 160 |
-
def _maybe_ignore_quant_config(self, quant_config: QuantizationConfig):
|
| 161 |
-
# Keep the MoonViT vision tower + trained projector in bf16; only the GLM
|
| 162 |
-
# text backbone is quantized. The GLM-5.2-NVFP4 checkpoint uses ModelOpt
|
| 163 |
-
# NVFP4 (modelopt_fp4) for the text Linears, but our vision/projector
|
| 164 |
-
# weights are bf16 -- so skip quant for them regardless of method.
|
| 165 |
-
from vllm.model_executor.layers.quantization.modelopt import (
|
| 166 |
-
ModelOptNvFp4Config,
|
| 167 |
-
ModelOptFp8Config,
|
| 168 |
-
)
|
| 169 |
-
|
| 170 |
-
if isinstance(
|
| 171 |
-
quant_config,
|
| 172 |
-
(
|
| 173 |
-
compressed_tensors.CompressedTensorsConfig,
|
| 174 |
-
ModelOptNvFp4Config,
|
| 175 |
-
ModelOptFp8Config,
|
| 176 |
-
),
|
| 177 |
-
):
|
| 178 |
-
return None
|
| 179 |
-
return quant_config
|
| 180 |
-
|
| 181 |
-
def _parse_and_validate_media_input(self, **kwargs: object):
|
| 182 |
-
pixel_values = kwargs.pop("pixel_values", None)
|
| 183 |
-
grid_thws = kwargs.pop("grid_thws", None)
|
| 184 |
-
if pixel_values is None:
|
| 185 |
-
return None
|
| 186 |
-
if isinstance(pixel_values, list):
|
| 187 |
-
pixel_values = torch.cat(pixel_values, dim=0)
|
| 188 |
-
if len(pixel_values.shape) == 5 or len(pixel_values.shape) == 3:
|
| 189 |
-
pixel_values = pixel_values.reshape(
|
| 190 |
-
pixel_values.shape[0] * pixel_values.shape[1], *pixel_values.shape[2:]
|
| 191 |
-
)
|
| 192 |
-
target_dtype = next(self.vision_tower.parameters()).dtype
|
| 193 |
-
pixel_values = pixel_values.to(target_dtype)
|
| 194 |
-
assert isinstance(grid_thws, torch.Tensor)
|
| 195 |
-
grid_thws = grid_thws.reshape(-1, grid_thws.shape[-1])
|
| 196 |
-
assert grid_thws.ndim == 2 and grid_thws.size(1) == 3
|
| 197 |
-
return {"pixel_values": pixel_values, "grid_thws": grid_thws}
|
| 198 |
-
|
| 199 |
-
def _process_media_input(self, media_input) -> list[torch.Tensor]:
|
| 200 |
-
return vision_tower_forward(
|
| 201 |
-
self.vision_tower,
|
| 202 |
-
media_input["pixel_values"],
|
| 203 |
-
media_input["grid_thws"],
|
| 204 |
-
mm_projector=self.mm_projector,
|
| 205 |
-
use_data_parallel=self.use_data_parallel,
|
| 206 |
-
)
|
| 207 |
-
|
| 208 |
-
def embed_multimodal(self, **kwargs: object) -> NestedTensors | None:
|
| 209 |
-
media_input = self._parse_and_validate_media_input(**kwargs)
|
| 210 |
-
if media_input is None:
|
| 211 |
-
return None
|
| 212 |
-
return self._process_media_input(media_input)
|
| 213 |
-
|
| 214 |
-
def forward(
|
| 215 |
-
self,
|
| 216 |
-
input_ids: torch.Tensor,
|
| 217 |
-
positions: torch.Tensor,
|
| 218 |
-
intermediate_tensors: IntermediateTensors | None = None,
|
| 219 |
-
inputs_embeds: torch.Tensor | None = None,
|
| 220 |
-
**kwargs: object,
|
| 221 |
-
) -> IntermediateTensors:
|
| 222 |
-
if intermediate_tensors is not None:
|
| 223 |
-
inputs_embeds = None
|
| 224 |
-
return self.language_model(
|
| 225 |
-
input_ids=input_ids,
|
| 226 |
-
positions=positions,
|
| 227 |
-
intermediate_tensors=intermediate_tensors,
|
| 228 |
-
inputs_embeds=inputs_embeds,
|
| 229 |
-
)
|
| 230 |
-
|
| 231 |
-
def compute_logits(self, hidden_states: torch.Tensor, **kwargs) -> torch.Tensor:
|
| 232 |
-
return self.language_model.compute_logits(hidden_states)
|
| 233 |
-
|
| 234 |
-
def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
|
| 235 |
-
loader = AutoWeightsLoader(self)
|
| 236 |
-
loaded = loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
|
| 237 |
-
# Sanity log: confirm the trained projector tensors were actually loaded
|
| 238 |
-
# (not left at init). Helps catch key-name mismatches.
|
| 239 |
-
proj = sorted(k for k in loaded if "mm_projector" in k)
|
| 240 |
-
logger.info("[glm5v] mm_projector keys loaded (%d): %s", len(proj), proj)
|
| 241 |
-
return loaded
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|