char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
71cc86fa4
char/aliceai-t5-moe
1from __future__import annotations 2 3import re 4 5from typing import Callable ,Iterable ,TYPE_CHECKING 6 7import torch 8 9if TYPE_CHECKING : 10from torch import Tensor 11 12from .base import ModelBase ,TextModel ,gguf ,logger 13 14from .deepseek import DeepseekV2Model 15 16 17@ ModelBase . register ( "Glm4ForCausalLM" , "Glm4vForConditionalGeneration" ) 18@ ModelBase . example ( "zai-org/GLM-4-9B-0414" ) 19class Glm4Model (TextModel ): 20model_arch = gguf .MODEL_ARCH .GLM4 21use_mrope = False 22partial_rotary_factor = 0.5 23 24def __init__ (self ,* args ,** kwargs ): 25super ().__init__ (* args ,** kwargs ) 26self .partial_rotary_factor = self .rope_parameters .get ("partial_rotary_factor" ,0.5 ) 27if "mrope_section" in self .rope_parameters : 28self .use_mrope = True 29logger .info ("Q/K weight will need to be permuted for M-RoPE" ) 30 31def set_vocab (self ): 32from transformers import AutoTokenizer 33tokenizer = AutoTokenizer .from_pretrained (self .dir_model ,trust_remote_code = True ) 34special_vocab = gguf .SpecialVocab (self .dir_model ,load_merges = True ) 35tokens ,toktypes ,tokpre = self .get_vocab_base () 36self .gguf_writer .add_tokenizer_model ("gpt2" ) 37self .gguf_writer .add_tokenizer_pre (tokpre ) 38self .gguf_writer .add_token_list (tokens ) 39self .gguf_writer .add_token_types (toktypes ) 40special_vocab = gguf .SpecialVocab (self .dir_model ,load_merges = True ) 41special_vocab ._set_special_token ("eos" ,tokenizer .get_added_vocab ()["<|endoftext|>" ])# ty: ignore[unresolved-attribute] 42special_vocab ._set_special_token ("eot" ,tokenizer .get_added_vocab ()["<|user|>" ])# ty: ignore[unresolved-attribute] 43special_vocab ._set_special_token ("unk" ,tokenizer .get_added_vocab ()["<|endoftext|>" ])# ty: ignore[unresolved-attribute] 44special_vocab ._set_special_token ("bos" ,tokenizer .get_added_vocab ()["<|endoftext|>" ])# ty: ignore[unresolved-attribute] 45special_vocab .add_to_gguf (self .gguf_writer ) 46 47def set_gguf_parameters (self ): 48super ().set_gguf_parameters () 49if (rope_dim := self .hparams .get ("head_dim" ))is None : 50rope_dim = self .hparams ["hidden_size" ]// self .hparams ["num_attention_heads" ] 51self .gguf_writer .add_rope_dimension_count (int (rope_dim * self .partial_rotary_factor )) 52 53@staticmethod 54def normal_to_neox (weights :Tensor ,n_head :int ,n_head_kv :int ,head_dim :int ,partial_rotary_factor :float )-> Tensor : 55orig_shape = weights .shape 56if len (orig_shape )== 1 : 57weights = weights .unsqueeze (1 )# [out_dim, 1] 58if len (weights .shape )!= 2 : 59raise ValueError ("Only 1D and 2D tensors are supported." ) 60n_effective_heads = weights .shape [0 ]// head_dim 61if n_head_kv is not None and n_effective_heads != n_head : 62if n_effective_heads != n_head_kv : 63raise AssertionError (f"Mismatch in effective heads: computed { n_effective_heads } , expected { n_head } or { n_head_kv } " ) 64rotary_dim = int (head_dim * partial_rotary_factor ) 65if rotary_dim % 2 != 0 : 66raise ValueError ("rotary_dim must be even." ) 67reshaped = weights .reshape (n_effective_heads ,head_dim ,- 1 ) 68rot_part = reshaped [:, :rotary_dim , :] 69non_rot_part = reshaped [:,rotary_dim :, :] 70permuted_rot = torch .cat ((rot_part [:, ::2 , :],rot_part [:,1 ::2 , :]),dim = 1 ) 71combined = torch .cat ((permuted_rot ,non_rot_part ),dim = 1 ) 72result = combined .reshape (weights .shape ) 73return result if len (orig_shape )!= 1 else result .squeeze (1 ) 74 75def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 76if self .use_mrope : 77n_head = self .hparams ["num_attention_heads" ] 78n_kv_head = self .hparams ["num_key_value_heads" ] 79n_embd = self .hparams ["hidden_size" ] 80head_dim = self .hparams .get ("head_dim" ,n_embd // n_head ) 81# because llama.cpp M-RoPE kernel only supports Neox ordering, we have to permute the weights here 82if name .endswith (("q_proj.weight" ,"q_proj.bias" )): 83data_torch = Glm4Model .normal_to_neox (data_torch ,n_head ,n_head ,head_dim ,self .partial_rotary_factor ) 84if name .endswith (("k_proj.weight" ,"k_proj.bias" )): 85data_torch = Glm4Model .normal_to_neox (data_torch ,n_head ,n_kv_head ,head_dim ,self .partial_rotary_factor ) 86yield from super ().modify_tensors (data_torch ,name ,bid ) 87 88 89@ ModelBase . register ( "GlmOcrForConditionalGeneration" ) 90@ ModelBase . example ( "zai-org/GLM-OCR" ) 91class GlmOCRModel (Glm4Model ): 92model_arch = gguf .MODEL_ARCH .GLM4 93use_mrope = False 94partial_rotary_factor = 0.5 95 96# Note: GLM-OCR is the same as GLM4, but with an extra NextN/MTP prediction layer 97 98def __init__ (self ,* args ,** kwargs ): 99super ().__init__ (* args ,** kwargs ) 100# GLM-OCR has num_hidden_layers + 1 actual layers (including NextN layer) 101self .block_count = self .hparams ["num_hidden_layers" ]+ self .hparams .get ("num_nextn_predict_layers" ,0 ) 102self .tensor_map = gguf .get_tensor_name_map (self .model_arch ,self .block_count ) 103 104def set_gguf_parameters (self ): 105super ().set_gguf_parameters () 106# NextN/MTP prediction layers 107if (num_nextn_predict_layers := self .hparams .get ("num_nextn_predict_layers" ))is not None : 108self .gguf_writer .add_nextn_predict_layers (num_nextn_predict_layers ) 109 110 111@ ModelBase . register ( "Glm4MoeForCausalLM" , "Glm4vMoeForConditionalGeneration" ) 112@ ModelBase . example ( "zai-org/GLM-4.5-Air" ) 113class Glm4MoeModel (TextModel ): 114model_arch = gguf .MODEL_ARCH .GLM4_MOE 115supports_mtp_export = True 116_n_main_layers :int | None = None 117 118def __init__ (self ,* args ,** kwargs ): 119super ().__init__ (* args ,** kwargs ) 120if not self .no_mtp : 121self .block_count += self .hparams .get ("num_nextn_predict_layers" ,0 ) 122self .tensor_map = gguf .get_tensor_name_map (self .model_arch ,self .block_count ) 123 124def index_tensors (self ,remote_hf_model_id :str | None = None ): 125hparams = {** self .hparams ,** self .hparams .get ("text_config" , {})} 126key = next ((k for k in ["n_layers" ,"num_hidden_layers" ,"n_layer" ,"num_layers" ]if k in hparams ),None ) 127 type(self )._n_main_layers = hparams .get (key ) 128return super ().index_tensors (remote_hf_model_id = remote_hf_model_id ) 129 130@classmethod 131def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 132if (titem := super ().filter_tensors (item ))is None : 133return None 134name ,gen = titem 135 136assert cls ._n_main_layers is not None 137is_mtp = (m := re .match (r"model\.layers\.(\d+)\." ,name ))is not None and int (m .group (1 ))>= cls ._n_main_layers 138 139if is_mtp and cls .no_mtp : 140return None 141if cls .mtp_only and not is_mtp and name not in ( 142"model.embed_tokens.weight" ,"model.norm.weight" ,"lm_head.weight" , 143 ): 144return None 145 146return name ,gen 147 148def set_vocab (self ): 149return self ._set_vocab_glm () 150 151def set_gguf_parameters (self ): 152super ().set_gguf_parameters () 153if (rope_dim := self .hparams .get ("head_dim" ))is None : 154rope_dim = ( 155self .hparams ["hidden_size" ]// self .hparams ["num_attention_heads" ] 156 ) 157self .gguf_writer .add_rope_dimension_count ( 158int (rope_dim * self .rope_parameters .get ("partial_rotary_factor" ,0.5 )) 159 ) 160 161# MoE parameters - Use only routed expert count (shared experts handled separately) 162if (n_routed_experts := self .hparams .get ("n_routed_experts" ))is not None : 163self .gguf_writer .add_expert_count (n_routed_experts ) 164if (moe_intermediate_size := self .hparams .get ("moe_intermediate_size" ))is not None : 165self .gguf_writer .add_expert_feed_forward_length (moe_intermediate_size ) 166if (n_shared_experts := self .hparams .get ("n_shared_experts" ))is not None : 167self .gguf_writer .add_expert_shared_count (n_shared_experts ) 168if (first_k_dense_replace := self .hparams .get ("first_k_dense_replace" ))is not None : 169self .gguf_writer .add_leading_dense_block_count (first_k_dense_replace ) 170 171# Expert gating function (sigmoid for GLM4_MOE) 172self .gguf_writer .add_expert_gating_func (gguf .ExpertGatingFuncType .SIGMOID ) 173 174# Routed scaling factor 175if (routed_scaling_factor := self .hparams .get ("routed_scaling_factor" ))is not None : 176self .gguf_writer .add_expert_weights_scale (routed_scaling_factor ) 177 178# Normalise topk probabilities 179if (norm_topk_prob := self .hparams .get ("norm_topk_prob" ))is not None : 180self .gguf_writer .add_expert_weights_norm (norm_topk_prob ) 181 182if not self .no_mtp and (num_nextn_predict_layers := self .hparams .get ("num_nextn_predict_layers" ))is not None : 183self .gguf_writer .add_nextn_predict_layers (num_nextn_predict_layers ) 184 185def prepare_metadata (self ,vocab_only :bool ): 186from_dir = self .fname_out .is_dir () 187super ().prepare_metadata (vocab_only = vocab_only ) 188 189if not self .mtp_only or not from_dir : 190return 191 192output_type :str = self .ftype .name .partition ("_" )[2 ] 193fname_default :str = gguf .naming_convention ( 194self .metadata .name ,self .metadata .basename ,self .metadata .finetune , 195self .metadata .version ,size_label = None ,output_type = output_type ,model_type = None ) 196self .fname_out = self .fname_out .parent / f"mtp- { fname_default } .gguf" 197 198_experts :list [dict [str ,Tensor ]]| None = None 199 200# note: unlike GLM4V non-MoE, we don't need to permute Q/K here since GLM4V_MOE uses Neox ordering already 201def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 202# Handle main token embedding (but not layer-specific NextN embeddings) 203if name == "model.embed_tokens.weight" and ".layers." not in name : 204yield from super ().modify_tensors (data_torch ,"token_embd.weight" ,bid ) 205return 206 207# Handle routed experts 208if name .find ("mlp.experts" )!= - 1 : 209n_experts = self .hparams ["n_routed_experts" ] 210assert bid is not None 211 212if self ._experts is None : 213self ._experts = [{}for _ in range (self .block_count )] 214 215self ._experts [bid ][name ]= data_torch 216 217if len (self ._experts [bid ])>= n_experts * 3 : 218# merge the experts into a single 3d tensor 219for w_name in ["down_proj" ,"gate_proj" ,"up_proj" ]: 220datas :list [Tensor ]= [] 221 222for xid in range (n_experts ): 223ename = f"model.layers. { bid } .mlp.experts. { xid } . { w_name } .weight" 224datas .append (self ._experts [bid ][ename ]) 225del self ._experts [bid ][ename ] 226 227data_torch = torch .stack (datas ,dim = 0 ) 228 229merged_name = f"model.layers. { bid } .mlp.experts. { w_name } .weight" 230 231yield from super ().modify_tensors (data_torch ,merged_name ,bid ) 232return 233else : 234return 235 236yield from super ().modify_tensors (data_torch ,name ,bid ) 237 238def prepare_tensors (self ): 239super ().prepare_tensors () 240if self ._experts is not None : 241# flatten `list[dict[str, Tensor]]` into `list[str]` 242experts = [k for d in self ._experts for k in d .keys ()] 243if len (experts )> 0 : 244raise ValueError (f"Unprocessed experts: { experts } " ) 245 246 247@ ModelBase . register ( "Glm4MoeLiteForCausalLM" ) 248@ ModelBase . example ( "zai-org/GLM-4.7-Flash" ) 249class Glm4MoeLiteModel (DeepseekV2Model ): 250model_arch = gguf .MODEL_ARCH .DEEPSEEK2 251skip_mtp = False 252supports_mtp_export = True 253_n_main_layers :int | None = None 254 255def set_vocab (self ): 256return self ._set_vocab_glm () 257 258def __init__ (self ,* args ,** kwargs ): 259super ().__init__ (* args ,** kwargs ) 260 261num_hidden_layers = self .hparams ["num_hidden_layers" ] 262self .num_nextn_predict_layers = self .hparams .get ("num_nextn_predict_layers" ,0 ) 263self .skip_mtp = self .no_mtp or self .num_nextn_predict_layers == 0 264 265if self .skip_mtp : 266self .block_count = num_hidden_layers 267else : 268self .block_count = num_hidden_layers + self .num_nextn_predict_layers 269 270self .tensor_map = gguf .get_tensor_name_map (self .model_arch ,self .block_count ) 271 272def set_gguf_parameters (self ): 273super ().set_gguf_parameters () 274 275if self .skip_mtp : 276return 277 278self .gguf_writer .add_nextn_predict_layers (self .num_nextn_predict_layers ) 279 280def index_tensors (self ,remote_hf_model_id :str | None = None ): 281 type(self )._n_main_layers = self .hparams ["num_hidden_layers" ] 282return super ().index_tensors (remote_hf_model_id = remote_hf_model_id ) 283 284@classmethod 285def filter_tensors (cls ,item ): 286if (titem := super ().filter_tensors (item ))is None : 287return None 288name ,gen = titem 289 290if cls ._n_main_layers is not None : 291match = re .match (r"model\.layers\.(\d+)\." ,name ) 292is_mtp = match is not None and int (match .group (1 ))>= cls ._n_main_layers 293if is_mtp and cls .no_mtp : 294return None 295if cls .mtp_only and not is_mtp and name not in ( 296"model.embed_tokens.weight" ,"model.norm.weight" ,"lm_head.weight" , 297 ): 298return None 299 300return name ,gen 301 302def prepare_metadata (self ,vocab_only :bool ): 303from_dir = self .fname_out .is_dir () 304super ().prepare_metadata (vocab_only = vocab_only ) 305 306if not self .mtp_only or not from_dir : 307return 308 309output_type :str = self .ftype .name .partition ("_" )[2 ] 310fname_default :str = gguf .naming_convention ( 311self .metadata .name ,self .metadata .basename ,self .metadata .finetune , 312self .metadata .version ,size_label = None ,output_type = output_type ,model_type = None ) 313self .fname_out = self .fname_out .parent / f"mtp- { fname_default } .gguf" 314 315 316@ ModelBase . register ( "GlmMoeDsaForCausalLM" ) 317@ ModelBase . example ( "zai-org/GLM-5.2" ) 318class GlmMoeDsaModel (DeepseekV2Model ): 319model_arch = gguf .MODEL_ARCH .GLM_DSA 320skip_mtp = False 321supports_mtp_export = True 322 323# Trunk layer count, stashed before indexing so the classmethod 324# filter_tensors can identify the appended NextN/MTP block (mirrors 325# HYV3Model / Step35Model). 326_n_main_layers :int | None = None 327 328def __init__ (self ,* args ,** kwargs ): 329super ().__init__ (* args ,** kwargs ) 330self .block_count = self .hparams ["num_hidden_layers" ] 331if not self .no_mtp : 332self .block_count += self .hparams .get ("num_nextn_predict_layers" ,0 ) 333self .tensor_map = gguf .get_tensor_name_map (self .model_arch ,self .block_count ) 334 335def index_tensors (self ,remote_hf_model_id :str | None = None ): 336 type(self )._n_main_layers = self .hparams ["num_hidden_layers" ] 337return super ().index_tensors (remote_hf_model_id = remote_hf_model_id ) 338 339@classmethod 340def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 341if (titem := super ().filter_tensors (item ))is None : 342return None 343name ,gen = titem 344 345# GLM-5.2 appends the NextN/MTP block past num_hidden_layers 346# (model.layers.78 -> blk.78 in the 79-block file). 347assert cls ._n_main_layers is not None 348is_mtp = (m := re .match (r"model\.layers\.(\d+)\." ,name ))is not None and int (m .group (1 ))>= cls ._n_main_layers 349 350# --no-mtp: drop the appended NextN block entirely. 351if is_mtp and cls .no_mtp : 352return None 353# --mtp: keep ONLY NextN-block tensors plus the shared embeddings/ 354# norm/lm_head (so the resulting GGUF carries just the draft head). 355if cls .mtp_only and not is_mtp and name not in ( 356"model.embed_tokens.weight" ,"model.norm.weight" ,"lm_head.weight" , 357 ): 358return None 359 360return name ,gen 361 362def set_vocab (self ): 363return self ._set_vocab_glm () 364 365def set_gguf_parameters (self ): 366super ().set_gguf_parameters () 367 368rope_dim = self .hparams ["qk_rope_head_dim" ] 369partial_rotary_factor = self .rope_parameters .get ("partial_rotary_factor" ,1.0 ) 370self .gguf_writer .add_rope_dimension_count (int (rope_dim * partial_rotary_factor )) 371 372# NextN/MTP prediction layers 373if not self .no_mtp and (num_nextn_predict_layers := self .hparams .get ("num_nextn_predict_layers" ))is not None : 374self .gguf_writer .add_nextn_predict_layers (num_nextn_predict_layers ) 375 376# DSA indexer parameters 377self .gguf_writer .add_indexer_head_count (self .hparams ["index_n_heads" ]) 378self .gguf_writer .add_indexer_key_length (self .hparams ["index_head_dim" ]) 379self .gguf_writer .add_indexer_top_k (self .hparams ["index_topk" ]) 380if (indexer_types := self .hparams .get ("indexer_types" ))is not None : 381indexer_types = [t == "full" for t in indexer_types ] 382self .gguf_writer .add_indexer_types (indexer_types ) 383 384 385@ ModelBase . register ( "SolarOpenForCausalLM" ) 386@ ModelBase . example ( "upstage/Solar-Open-100B" ) 387class SolarOpenModel (Glm4MoeModel ): 388model_arch = gguf .MODEL_ARCH .GLM4_MOE 389supports_mtp_export = False 390 391def set_vocab (self ): 392from transformers import AutoTokenizer 393tokenizer = AutoTokenizer .from_pretrained (self .dir_model ) 394special_vocab = gguf .SpecialVocab (self .dir_model ,load_merges = True ) 395tokens ,toktypes ,tokpre = self .get_vocab_base () 396self .gguf_writer .add_tokenizer_model ("gpt2" ) 397self .gguf_writer .add_tokenizer_pre (tokpre ) 398self .gguf_writer .add_token_list (tokens ) 399self .gguf_writer .add_token_types (toktypes ) 400special_vocab ._set_special_token ("eos" ,tokenizer .get_added_vocab ()["<|endoftext|>" ])# ty: ignore[unresolved-attribute] 401special_vocab ._set_special_token ("eot" ,tokenizer .get_added_vocab ()["<|endoftext|>" ])# ty: ignore[unresolved-attribute] 402special_vocab ._set_special_token ("unk" ,tokenizer .get_added_vocab ()["<unk>" ])# ty: ignore[unresolved-attribute] 403special_vocab ._set_special_token ("bos" ,tokenizer .get_added_vocab ()["<|startoftext|>" ])# ty: ignore[unresolved-attribute] 404special_vocab .add_to_gguf (self .gguf_writer )