char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
fa88ae936
char/aliceai-t5-moe
1from __future__import annotations 2 3from typing import Callable ,Iterable ,TYPE_CHECKING 4 5import torch 6 7if TYPE_CHECKING : 8from torch import Tensor 9 10from .base import MmprojModel ,ModelBase ,TextModel ,gguf ,logger 11 12from .llama import LlamaModel 13from .qwen import Qwen3_5TextModel 14 15 16@ ModelBase . register ( "MiniCPMForCausalLM" ) 17@ ModelBase . example ( "openbmb/MiniCPM-2B-sft-bf16" ) 18class MiniCPMModel (TextModel ): 19model_arch = gguf .MODEL_ARCH .MINICPM 20 21def set_gguf_parameters (self ): 22super ().set_gguf_parameters () 23embedding_scale = float (self .hparams ["scale_emb" ]) 24self .gguf_writer .add_embedding_scale (embedding_scale ) 25logger .info (f"gguf: (minicpm) embedding_scale = { embedding_scale } " ) 26residual_scale = self .hparams ["scale_depth" ]/ self .hparams ["num_hidden_layers" ]** 0.5 27self .gguf_writer .add_residual_scale (residual_scale ) 28logger .info (f"gguf: (minicpm) residual_scale = { residual_scale } " ) 29logit_scale = self .hparams ["hidden_size" ]/ self .hparams ["dim_model_base" ] 30self .gguf_writer .add_logit_scale (logit_scale ) 31logger .info (f"gguf: (minicpm) logit_scale = { logit_scale } " ) 32 33def generate_extra_tensors (self )-> Iterable [tuple [str ,Tensor ]]: 34rope_dims = self .hparams ["hidden_size" ]// self .hparams ["num_attention_heads" ] 35 36long_factors = self .rope_parameters .get ('long_factor' ) 37short_factors = self .rope_parameters .get ('short_factor' ) 38if long_factors or short_factors : 39if long_factors is None or short_factors is None : 40raise KeyError ('Missing the required key rope_scaling.long_factor or rope_scaling_short_factor' ) 41 42if len (long_factors )!= len (short_factors )or len (long_factors )!= rope_dims / 2 : 43raise ValueError (f'The length of rope long and short factors must be { rope_dims / 2 } ' ) 44 45yield (self .format_tensor_name (gguf .MODEL_TENSOR .ROPE_FACTORS_LONG ),torch .tensor (long_factors ,dtype = torch .float32 )) 46yield (self .format_tensor_name (gguf .MODEL_TENSOR .ROPE_FACTORS_SHORT ),torch .tensor (short_factors ,dtype = torch .float32 )) 47 48def set_vocab (self ): 49self ._set_vocab_sentencepiece () 50 51def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 52n_head = self .hparams ["num_attention_heads" ] 53n_kv_head = self .hparams .get ("num_key_value_heads" ) 54 55# HF models permute some of the tensors, so we need to undo that 56if name .endswith (("q_proj.weight" )): 57data_torch = LlamaModel .permute (data_torch ,n_head ,n_head ) 58if name .endswith (("k_proj.weight" )): 59data_torch = LlamaModel .permute (data_torch ,n_head ,n_kv_head ) 60 61yield from super ().modify_tensors (data_torch ,name ,bid ) 62 63 64@ ModelBase . register ( "MiniCPM3ForCausalLM" ) 65@ ModelBase . example ( "openbmb/MiniCPM3-4B" ) 66class MiniCPM3Model (TextModel ): 67model_arch = gguf .MODEL_ARCH .MINICPM3 68 69def set_gguf_parameters (self ): 70hparams = self .hparams 71 72self .gguf_writer .add_file_type (self .ftype ) 73self .gguf_writer .add_context_length (hparams ["max_position_embeddings" ]) 74self .gguf_writer .add_embedding_length (hparams ["hidden_size" ]) 75self .gguf_writer .add_block_count (self .block_count ) 76self .gguf_writer .add_feed_forward_length (hparams ["intermediate_size" ]) 77self .gguf_writer .add_head_count (hparams ["num_attention_heads" ]) 78self .gguf_writer .add_head_count_kv (hparams ["num_key_value_heads" ]) 79self .gguf_writer .add_layer_norm_rms_eps (hparams ["rms_norm_eps" ]) 80self .gguf_writer .add_vocab_size (hparams ["vocab_size" ]) 81if "q_lora_rank" in hparams and hparams ["q_lora_rank" ]is not None : 82self .gguf_writer .add_q_lora_rank (hparams ["q_lora_rank" ]) 83self .gguf_writer .add_kv_lora_rank (hparams ["kv_lora_rank" ]) 84self .gguf_writer .add_key_length (hparams ["qk_nope_head_dim" ]+ hparams ["qk_rope_head_dim" ]) 85self .gguf_writer .add_rope_dimension_count (hparams ["qk_rope_head_dim" ]) 86 87def generate_extra_tensors (self )-> Iterable [tuple [str ,Tensor ]]: 88long_factors = self .rope_parameters .get ('long_factor' ) 89short_factors = self .rope_parameters .get ('short_factor' ) 90if long_factors or short_factors : 91rope_dims = self .hparams ["qk_rope_head_dim" ] 92 93if long_factors is None or short_factors is None : 94raise KeyError ('Missing the required key rope_scaling.long_factor or rope_scaling_short_factor' ) 95 96if len (long_factors )!= len (short_factors )or len (long_factors )!= rope_dims / 2 : 97raise ValueError (f'The length of rope long and short factors must be { rope_dims / 2 } ' ) 98 99yield (self .format_tensor_name (gguf .MODEL_TENSOR .ROPE_FACTORS_LONG ),torch .tensor (long_factors ,dtype = torch .float32 )) 100yield (self .format_tensor_name (gguf .MODEL_TENSOR .ROPE_FACTORS_SHORT ),torch .tensor (short_factors ,dtype = torch .float32 )) 101 102def set_vocab (self ): 103self ._set_vocab_sentencepiece () 104 105def _reverse_hf_permute (self ,weights :Tensor ,n_head :int ,n_kv_head :int | None = None )-> Tensor : 106if n_kv_head is not None and n_head != n_kv_head : 107n_head //= n_kv_head 108 109return ( 110weights .reshape (n_head ,2 ,weights .shape [0 ]// n_head // 2 ,* weights .shape [1 :]) 111 .swapaxes (1 ,2 ) 112 .reshape (weights .shape ) 113 ) 114 115 116# MiniCPM-V 4.6: text tower is Qwen3.5 (linear+full hybrid attention) wrapped under 117# `model.language_model.*`; vision tower is SigLIP + a window-attention ViT merger 118# + a final DownsampleMLP merger. The same HF arch is registered twice below: once as 119# the LM (text mode) and once as the mmproj (vision mode), mirroring the Qwen3-VL setup. 120 121@ ModelBase . register ( "MiniCPMV4_6ForConditionalGeneration" ) 122@ ModelBase . example ( "openbmb/MiniCPM-V-4_6" ) 123class MiniCPMV4_6TextModel (Qwen3_5TextModel ): 124model_arch = gguf .MODEL_ARCH .QWEN35 125 126@classmethod 127def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 128name ,gen = item 129 130if name .startswith ("model.merger." ): 131return None 132# MTP tensors are not used at inference yet; align with Qwen3Next behaviour 133if name .startswith ("mtp" ): 134return None 135 136return super ().filter_tensors (item ) 137 138 139@ ModelBase . register ( "MiniCPMV4_6ForConditionalGeneration" ) 140@ ModelBase . example ( "openbmb/MiniCPM-V-4_6" ) 141class MiniCPMV4_6VisionModel (MmprojModel ): 142def __init__ (self ,* args ,** kwargs ): 143super ().__init__ (* args ,** kwargs ) 144self .downsample_mode = self .preprocessor_config .get ("downsample_mode" ,"16x" ) 145if self .downsample_mode not in {"4x" ,"16x" }: 146raise ValueError (f"Unsupported downsample mode: { self . downsample_mode } " ) 147if self .downsample_mode == "4x" : 148self .model_tensors = { 149name :tensor for name ,tensor in self .model_tensors .items () 150if ".vit_merger." not in name 151 } 152 153if self .hparams_vision is not None : 154# In MiniCPM-V 4.6 `vision_config.image_size` (980) describes the SigLIP 155# positional embedding bucket grid (70 x 70), while the per-slice processing 156# resolution is the preprocessor's `scale_resolution` (typically 448). 157# The CLIP loader in tools/mtmd/clip.cpp consumes `clip.vision.image_size` 158# as the slice size and warmup resolution, so report `scale_resolution` there 159# to match the upstream MiniCPMV4_6ImageProcessorPil slicing rules. 160scale_resolution = self .preprocessor_config .get ("scale_resolution" ) 161if scale_resolution is not None : 162self .hparams_vision ["image_size" ]= int (scale_resolution ) 163 164def set_gguf_parameters (self ): 165super ().set_gguf_parameters () 166assert self .hparams_vision is not None 167 168# projector type string is consumed by clip_projector_type_from_string() in clip.cpp 169# (mapped to PROJECTOR_TYPE_MINICPMV4_6). 170self .gguf_writer .add_clip_projector_type (gguf .VisionProjectorType .MINICPMV4_6 ) 171 172self .gguf_writer .add_vision_projector_scale_factor ( 1732 if self .downsample_mode == "4x" else 4 ) 174 175# borrow wa_layer_indexes for vit_merger insertion point 176insert_layer_id = int (self .global_config .get ( 177"insert_layer_id" ,self .hparams_vision .get ("insert_layer_id" ,6 ))) 178self .gguf_writer .add_vision_wa_layer_indexes ([insert_layer_id ]) 179 180# SigLIP vision body uses gelu_pytorch_tanh, which matches ggml_gelu (tanh approx). 181self .gguf_writer .add_vision_use_gelu (True ) 182self .gguf_writer .add_vision_attention_layernorm_eps ( 183self .hparams_vision .get ("layer_norm_eps" ,1e-6 )) 184 185@classmethod 186def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 187name ,gen = item 188 189# lm_head / MTP -> belong to the LM file 190if name .startswith (("lm_head." ,"mtp" )): 191return None 192 193return super ().filter_tensors (item )