char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
fa88ae936
char/aliceai-t5-moe
1from __future__import annotations 2 3import json 4import math 5 6from typing import Callable ,Iterable ,TYPE_CHECKING 7 8import numpy as np 9import torch 10 11if TYPE_CHECKING : 12from torch import Tensor 13 14from .base import ModelBase ,TextModel ,gguf ,logger 15 16 17@ ModelBase . register ( 18"LLaMAForCausalLM" , 19"LlamaForCausalLM" , 20"MistralForCausalLM" , 21"MixtralForCausalLM" , 22"VLlama3ForCausalLM" , 23"LlavaForConditionalGeneration" , 24"VoxtralForConditionalGeneration" , 25"LlamaForCausalLMEagle3" , 26"Eagle3LlamaForCausalLM" , 27"Eagle3Speculator" , 28"Eagle3DraftModel" , 29"IQuestCoderForCausalLM" , 30"LlamaModel" ) 31# [TAG_HF_EXAMPLE_GATED] meta-llama/Llama-3.2-1B-Instruct is gated 32@ ModelBase . example ( "unsloth/Llama-3.2-1B-Instruct" , "mistralai/Mistral-7B-Instruct-v0.3" , "mistralai/Mixtral-8x7B-Instruct-v0.1" ) 33class LlamaModel (TextModel ): 34model_arch = gguf .MODEL_ARCH .LLAMA 35undo_permute = True 36 37def __init__ (self ,* args ,** kwargs ): 38super ().__init__ (* args ,** kwargs ) 39# fix for SmolVLM2, missing `num_attention_heads` in config.json 40if self .hf_arch == "VLlama3ForCausalLM" : 41self .hparams ["num_attention_heads" ]= self .hparams .get ("num_attention_heads" ,32 ) 42# Mistral consolidated format has no config.json; origin_hf_arch is HF-only. 43if self .is_mistral_format : 44self .origin_hf_arch = None 45else : 46hparams = ModelBase .load_hparams (self .dir_model ,is_mistral_format = False ) 47self .origin_hf_arch = hparams .get ('architectures' , [None ])[0 ] 48 49# Detect eagle3 draft checkpoint by hparams (some models don't use a distinct HF arch name) 50if "draft_vocab_size" in self .hparams and self .hparams ["num_hidden_layers" ]== 1 : 51self .is_eagle3 = True 52self .model_arch = gguf .MODEL_ARCH .EAGLE3 53logger .info ("Detected EAGLE-3 draft model, switching to EAGLE3 architecture" ) 54# Re-initialize tensor_map with eagle3 architecture 55self .tensor_map = gguf .get_tensor_name_map (self .model_arch ,self .block_count ) 56# Update gguf_writer architecture 57self .gguf_writer .arch = gguf .MODEL_ARCH_NAMES [self .model_arch ] 58self .gguf_writer .add_architecture () 59if self .target_model_dir is None : 60raise ValueError ( 61"EAGLE-3 model requires --target-model-dir to be specified. " 62"Please provide the path to the target model directory to read config.json" 63 ) 64# Read both eagle3 raw config and target model config 65with open (self .dir_model / "config.json" ,'r' ,encoding = 'utf-8' )as f : 66eagle3_raw_config = json .load (f ) 67with open (self .target_model_dir / "config.json" ,'r' ,encoding = 'utf-8' )as f : 68target_config = json .load (f ) 69 70if "text_config" in target_config : 71target_config = {** target_config ,** target_config ["text_config" ]} 72self .target_vocab_size = target_config ["vocab_size" ] 73 74# target_layers: use the eagle3 config's explicit aux hidden-state layer ids 75# if present, else derive from the target layer count. 76target_num_layers = target_config ["num_hidden_layers" ] 77aux_layer_ids = eagle3_raw_config .get ("eagle_aux_hidden_state_layer_ids" ) 78if aux_layer_ids : 79target_layers = aux_layer_ids 80else : 81target_layers = [2 ,target_num_layers // 2 ,target_num_layers - 3 ] 82logger .info (f"EAGLE-3: target_layers = { target_layers } (target model has { target_num_layers } layers)" ) 83self .gguf_writer .add_target_layers (target_layers ) 84 85# target_hidden_size: prefer eagle3 config, fallback to target config 86if eagle3_raw_config .get ("target_hidden_size" )is not None : 87target_hidden_size = eagle3_raw_config ["target_hidden_size" ] 88src = "EAGLE-3 config" 89else : 90target_hidden_size = target_config ["hidden_size" ] 91src = "target model config" 92logger .info (f"EAGLE-3: target_hidden_size = { target_hidden_size } (from { src } )" ) 93self .gguf_writer .add_target_hidden_size (target_hidden_size ) 94 95# norm_before_residual (RedHat-style eagle3 specific) 96norm_before_residual = eagle3_raw_config .get ("norm_before_residual" ,False ) 97logger .info (f"EAGLE-3: norm_before_residual = { norm_before_residual } " ) 98self .gguf_writer .add_norm_before_residual (norm_before_residual ) 99 100# norm_before_fc: RMSNorm applied to the fused target features before the 101# fc projection (e.g. nvidia/gpt-oss-120b-Eagle3-v3) 102norm_before_fc = eagle3_raw_config .get ("norm_before_fc" ,False ) 103logger .info (f"EAGLE-3: norm_before_fc = { norm_before_fc } " ) 104self .gguf_writer .add_norm_before_fc (norm_before_fc ) 105 106def set_vocab (self ): 107# eagle3: use tokenizer from target model if provided 108original_dir_model = None 109if getattr (self ,'is_eagle3' ,False ): 110assert self .target_model_dir is not None 111logger .info (f"EAGLE-3: Using tokenizer from target model: { self . target_model_dir } " ) 112original_dir_model = self .dir_model 113self .dir_model = self .target_model_dir 114 115if self .origin_hf_arch == "GlmasrModel" : 116return self ._set_vocab_glmedge () 117 118if self .is_mistral_format : 119return self ._set_vocab_mistral () 120 121path_tekken_json = self .dir_model / "tekken.json" 122path_tokenizer_json = self .dir_model / "tokenizer.json" 123if path_tekken_json .is_file ()and not path_tokenizer_json .is_file (): 124return self ._set_vocab_mistral () 125 126tokenizer_config_file = self .dir_model / 'tokenizer_config.json' 127if tokenizer_config_file .is_file (): 128with open (tokenizer_config_file ,"r" ,encoding = "utf-8" )as f : 129tokenizer_config_json = json .load (f ) 130if (add_prefix_space := tokenizer_config_json .get ("add_prefix_space" ))is not None : 131self .gguf_writer .add_add_space_prefix (add_prefix_space ) 132if tokenizer_config_json .get ("tokenizer_class" )== "HybridDNATokenizer" : 133return self ._set_vocab_hybriddna () 134 135try : 136self ._set_vocab_sentencepiece () 137except FileNotFoundError : 138try : 139self ._set_vocab_llama_hf () 140except (FileNotFoundError ,TypeError ): 141# Llama 3 142self ._set_vocab_gpt2 () 143 144# Apply to CodeLlama only (and ignore for Llama 3 with a vocab size of 128256) 145if self .hparams .get ("vocab_size" ,32000 )== 32016 : 146special_vocab = gguf .SpecialVocab ( 147self .dir_model ,load_merges = False , 148special_token_types = ['prefix' ,'suffix' ,'middle' ,'eot' ] 149 ) 150special_vocab ._set_special_token ("prefix" ,32007 ) 151special_vocab ._set_special_token ("suffix" ,32008 ) 152special_vocab ._set_special_token ("middle" ,32009 ) 153special_vocab ._set_special_token ("eot" ,32010 ) 154special_vocab .add_to_gguf (self .gguf_writer ) 155 156# Apply to granite small models only 157if self .hparams .get ("vocab_size" ,32000 )== 49152 : 158self .gguf_writer .add_add_bos_token (False ) 159 160# eagle3: Restore original dir_model 161if original_dir_model is not None : 162self .dir_model = original_dir_model 163 164def set_gguf_parameters (self ): 165super ().set_gguf_parameters () 166hparams = self .hparams 167 168if not self .is_mistral_format : 169self .gguf_writer .add_vocab_size (hparams ["vocab_size" ]) 170 171if (rope_dim := hparams .get ("head_dim" ))is None : 172rope_dim = hparams ["hidden_size" ]// hparams ["num_attention_heads" ] 173self .gguf_writer .add_rope_dimension_count (rope_dim ) 174 175@staticmethod 176def permute (weights :Tensor ,n_head :int ,n_head_kv :int | None ): 177if n_head_kv is not None and n_head != n_head_kv : 178n_head = n_head_kv 179return (weights .reshape (n_head ,2 ,weights .shape [0 ]// n_head // 2 ,* weights .shape [1 :]) 180 .swapaxes (1 ,2 ) 181 .reshape (weights .shape )) 182 183def _repack_nvfp4 (self ,name :str ,weight :Tensor ,scale :Tensor ,scale2 :Tensor ,input_scale :Tensor ): 184# Mirror the BF16 Q/K RoPE permutation site in modify_tensors; the NVFP4 path bypasses it. 185if self .undo_permute : 186n_head = self .find_hparam (["n_heads" ,"num_attention_heads" ],optional = True ) 187n_kv_head = self .find_hparam (["n_kv_heads" ,"num_key_value_heads" ],optional = True ) 188if n_head is not None : 189if name .endswith ("q_proj.weight" ): 190weight = LlamaModel .permute (weight ,n_head ,n_head ) 191scale = LlamaModel .permute (scale ,n_head ,n_head ) 192elif name .endswith ("k_proj.weight" ): 193weight = LlamaModel .permute (weight ,n_head ,n_kv_head ) 194scale = LlamaModel .permute (scale ,n_head ,n_kv_head ) 195super ()._repack_nvfp4 (name ,weight ,scale ,scale2 ,input_scale ) 196 197_experts :list [dict [str ,Tensor ]]| None = None 198 199@classmethod 200def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 201name ,gen = item 202 203if "text_model." in name : 204name = name .replace ("text_model." ,"" )# for SmolVLM 205 206return super ().filter_tensors ((name ,gen )) 207 208def index_tensors (self ,remote_hf_model_id :str | None = None )-> dict [str ,Callable [[],Tensor ]]: 209tensors = super ().index_tensors (remote_hf_model_id ) 210 211# Handle Eagle3Speculator nested config 212if "transformer_layer_config" in self .hparams : 213self .hparams = {** self .hparams ,** self .hparams ["transformer_layer_config" ]} 214 215# eagle3 detection 216if "draft_vocab_size" in self .hparams and self .hparams ["num_hidden_layers" ]== 1 : 217logger .info ("EAGLE-3: renaming midlayer.* / layers.0.* to model.layers.0.*" ) 218new_tensors = {} 219for name ,gen in tensors .items (): 220if name .startswith ("midlayer." ): 221new_name = "model.layers.0." + name [len ("midlayer." ):] 222new_tensors [new_name ]= gen 223elif name .startswith ("layers.0." ):# Eagle3Speculator format 224new_name = "model." + name 225new_tensors [new_name ]= gen 226else : 227new_tensors [name ]= gen 228return new_tensors 229 230return tensors 231 232def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 233# eagle3: special tensors that bypass standard llama mapping 234if getattr (self ,'is_eagle3' ,False ): 235if name == "fc.weight" : 236yield (name ,data_torch ) 237return 238if name == "input_norm.weight" : 239yield (self .format_tensor_name (gguf .MODEL_TENSOR .ENC_OUTPUT_NORM ),data_torch ) 240return 241if name == "d2t" : 242# store for manual int64 handling in prepare_tensors (avoid F32 conversion) 243if not hasattr (self ,'_eagle3_int_tensors' ): 244self ._eagle3_int_tensors = {} 245self ._eagle3_int_tensors [name ]= data_torch 246return 247if name == "t2d" : 248# not used at runtime, skip 249return 250if name .endswith (".hidden_norm.weight" ): 251yield (self .format_tensor_name (gguf .MODEL_TENSOR .ATTN_NORM_2 ,bid ),data_torch ) 252return 253 254n_head = self .find_hparam (["n_heads" ,"num_attention_heads" ]) 255n_kv_head = self .find_hparam (["n_kv_heads" ,"num_key_value_heads" ]) 256 257if self .hf_arch == "LlamaModel" : 258name = "model." + name 259 260if self .undo_permute : 261if name .endswith (("q_proj.weight" ,"q_proj.bias" )): 262data_torch = LlamaModel .permute (data_torch ,n_head ,n_head ) 263if name .endswith (("k_proj.weight" ,"k_proj.bias" )): 264data_torch = LlamaModel .permute (data_torch ,n_head ,n_kv_head ) 265 266# process the experts separately 267if name .find ("block_sparse_moe.experts" )!= - 1 : 268n_experts = self .hparams ["num_local_experts" ] 269 270assert bid is not None 271 272if self ._experts is None : 273self ._experts = [{}for _ in range (self .block_count )] 274 275self ._experts [bid ][name ]= data_torch 276 277if len (self ._experts [bid ])>= n_experts * 3 : 278# merge the experts into a single 3d tensor 279for wid in ["w1" ,"w2" ,"w3" ]: 280datas :list [Tensor ]= [] 281 282for xid in range (n_experts ): 283ename = f"model.layers. { bid } .block_sparse_moe.experts. { xid } . { wid } .weight" 284datas .append (self ._experts [bid ][ename ]) 285del self ._experts [bid ][ename ] 286 287data_torch = torch .stack (datas ,dim = 0 ) 288 289merged_name = f"layers. { bid } .feed_forward.experts. { wid } .weight" 290 291yield from super ().modify_tensors (data_torch ,merged_name ,bid ) 292return 293else : 294return 295 296yield from super ().modify_tensors (data_torch ,name ,bid ) 297 298def generate_extra_tensors (self )-> Iterable [tuple [str ,Tensor ]]: 299if rope_params := self .rope_parameters .get ("full_attention" ,self .rope_parameters ): 300if rope_params .get ("rope_type" ,'' ).lower ()== "llama3" : 301base = rope_params .get ("rope_theta" ,10000.0 ) 302if (dim := self .hparams .get ("head_dim" ))is None : 303dim = self .hparams ["hidden_size" ]// self .hparams ["num_attention_heads" ] 304freqs = 1.0 / (base ** (torch .arange (0 ,dim ,2 ,dtype = torch .float32 )/ dim )) 305 306factor = rope_params .get ("factor" ,8.0 ) 307low_freq_factor = rope_params .get ("low_freq_factor" ,1.0 ) 308high_freq_factor = rope_params .get ("high_freq_factor" ,4.0 ) 309old_context_len = rope_params .get ("original_max_position_embeddings" ,8192 ) 310 311low_freq_wavelen = old_context_len / low_freq_factor 312high_freq_wavelen = old_context_len / high_freq_factor 313# assert low_freq_wavelen != high_freq_wavelen # Errors for Llama4 314 315rope_factors = [] 316for freq in freqs : 317wavelen = 2 * math .pi / freq 318if wavelen < high_freq_wavelen : 319rope_factors .append (1 ) 320elif wavelen > low_freq_wavelen : 321rope_factors .append (factor ) 322else : 323smooth = (old_context_len / wavelen - low_freq_factor )/ (high_freq_factor - low_freq_factor ) 324rope_factors .append (1 / ((1 - smooth )/ factor + smooth )) 325 326yield (self .format_tensor_name (gguf .MODEL_TENSOR .ROPE_FREQS ),torch .tensor (rope_factors ,dtype = torch .float32 )) 327 328def prepare_tensors (self ): 329# eagle3: collect d2t original dtype before parent converts tensors to F32 330eagle3_original_dtypes = {} 331if getattr (self ,'is_eagle3' ,False ): 332for name ,data_torch in self .get_tensors (): 333if name == "d2t" : 334eagle3_original_dtypes [name ]= data_torch .dtype 335 336super ().prepare_tensors () 337 338# eagle3: write d2t as absolute target token ids 339if getattr (self ,'is_eagle3' ,False )and hasattr (self ,'_eagle3_int_tensors' ): 340for name ,data_torch in self ._eagle3_int_tensors .items (): 341old_dtype = eagle3_original_dtypes .get (name ,data_torch .dtype ) 342data = data_torch .to (torch .int64 ).cpu ().numpy () 343if name == "d2t" : 344data = data .reshape (- 1 ) 345data = data + np .arange (data .size ,dtype = np .int64 ) 346if np .any ((data < 0 )| (data >= self .target_vocab_size )): 347raise ValueError (f"EAGLE-3 d2t target ids out of range for target vocab size { self . target_vocab_size } " ) 348if np .unique (data ).size != data .size : 349raise ValueError ("EAGLE-3 d2t contains duplicate target ids" ) 350data_qtype = gguf .GGMLQuantizationType .I64 351 352shape_str = f"{{ { ', ' . join ( str ( n ) for n in reversed ( data . shape )) } }}" 353logger .info (f" { name + ',' :<30 } { old_dtype } --> { data_qtype . name } , shape = { shape_str } " ) 354self .gguf_writer .add_tensor (name ,data ,raw_dtype = data_qtype ) 355 356if self ._experts is not None : 357# flatten `list[dict[str, Tensor]]` into `list[str]` 358experts = [k for d in self ._experts for k in d .keys ()] 359if len (experts )> 0 : 360raise ValueError (f"Unprocessed experts: { experts } " ) 361 362 363@ ModelBase . register ( "ArceeForCausalLM" ) 364@ ModelBase . example ( "arcee-ai/AFM-4.5B" ) 365class ArceeModel (LlamaModel ): 366model_arch = gguf .MODEL_ARCH .ARCEE 367 368def set_gguf_parameters (self ): 369super ().set_gguf_parameters () 370self ._try_set_pooling_type () 371 372 373@ ModelBase . register ( 374"Llama4ForConditionalGeneration" , 375"Llama4ForCausalLM" , 376) 377# [TAG_HF_EXAMPLE_GATED] meta-llama/Llama-4-Scout-17B-16E-Instruct is gated 378@ ModelBase . example ( "unsloth/Llama-4-Scout-17B-16E-Instruct" ) 379class Llama4Model (LlamaModel ): 380model_arch = gguf .MODEL_ARCH .LLAMA4 381undo_permute = False 382 383def __init__ (self ,* args ,** kwargs ): 384super ().__init__ (* args ,** kwargs ) 385# IMPORTANT: the normal "intermediate_size" is renamed to "intermediate_size_mlp", we need to undo this 386self .hparams ["intermediate_size_moe" ]= self .hparams ["intermediate_size" ] 387self .hparams ["intermediate_size" ]= self .hparams ["intermediate_size_mlp" ] 388 389def set_vocab (self ): 390self ._set_vocab_gpt2 () 391 392def set_gguf_parameters (self ): 393super ().set_gguf_parameters () 394self .gguf_writer .add_interleave_moe_layer_step (self .hparams ["interleave_moe_layer_step" ]) 395self .gguf_writer .add_expert_feed_forward_length (self .hparams ["intermediate_size_moe" ]) 396if "layer_types" in self .hparams : 397if all (lt == "full_attention" for lt in self .hparams ["layer_types" ]): 398# all layers are full attention (for MobileLLM), disable swa 399self .gguf_writer .add_sliding_window (0 ) 400 401def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None ): 402# split the gate_up into gate and up 403if "gate_up_proj" in name : 404name_up = name .replace ("gate_up_proj" ,"up_proj.weight" ) 405name_gate = name .replace ("gate_up_proj" ,"gate_proj.weight" ) 406dim_half = data_torch .shape [- 1 ]// 2 407gate_proj_weight ,up_proj_weight = data_torch .transpose (- 1 ,- 2 ).split (dim_half ,dim =- 2 ) 408yield from super ().modify_tensors (gate_proj_weight ,name_gate ,bid ) 409yield from super ().modify_tensors (up_proj_weight ,name_up ,bid ) 410return 411 412if name .endswith ("down_proj" ): 413name += ".weight" 414data_torch = data_torch .transpose (- 1 ,- 2 ) 415 416yield from super ().modify_tensors (data_torch ,name ,bid ) 417 418 419@ ModelBase . register ( "LlamaBidirectionalModel" ) 420@ ModelBase . example ( "nvidia/llama-embed-nemotron-8b" ) 421class LlamaEmbedNemotronModel (LlamaModel ): 422model_arch = gguf .MODEL_ARCH .LLAMA_EMBED 423 424 425@ ModelBase . register ( "SmolLM3ForCausalLM" ) 426@ ModelBase . example ( "HuggingFaceTB/SmolLM3-3B" ) 427class SmolLM3Model (LlamaModel ): 428model_arch = gguf .MODEL_ARCH .SMOLLM3 429 430 431@ ModelBase . register ( "ApertusForCausalLM" ) 432@ ModelBase . example ( "swiss-ai/Apertus-8B-Instruct-2509" ) 433class ApertusModel (LlamaModel ): 434model_arch = gguf .MODEL_ARCH .APERTUS 435undo_permute = False 436 437_alpha_n = {} 438_alpha_p = {} 439_beta = {} 440_eps = {} 441 442def modify_tensors (self ,data_torch ,name ,bid ): 443# Handle xIELU activation parameters 444n_layers = self .hparams ["num_hidden_layers" ] 445if name .endswith (".act_fn.alpha_n" ): 446self ._alpha_n [bid ]= data_torch .to ("cpu" ).float ().item () 447if (len (self ._alpha_n )== n_layers ): 448self .gguf_writer .add_xielu_alpha_n ([self ._alpha_n [k ]for k in sorted (self ._alpha_n )]) 449return 450if name .endswith (".act_fn.alpha_p" ): 451self ._alpha_p [bid ]= data_torch .to ("cpu" ).float ().item () 452if (len (self ._alpha_p )== n_layers ): 453self .gguf_writer .add_xielu_alpha_p ([self ._alpha_p [k ]for k in sorted (self ._alpha_p )]) 454return 455if name .endswith (".act_fn.beta" ): 456self ._beta [bid ]= data_torch .to ("cpu" ).float ().item () 457if (len (self ._beta )== n_layers ): 458self .gguf_writer .add_xielu_beta ([self ._beta [k ]for k in sorted (self ._beta )]) 459return 460if name .endswith (".act_fn.eps" ): 461self ._eps [bid ]= data_torch .to ("cpu" ).float ().item () 462if (len (self ._eps )== n_layers ): 463self .gguf_writer .add_xielu_eps ([self ._eps [k ]for k in sorted (self ._eps )]) 464return 465 466yield from super ().modify_tensors (data_torch ,name ,bid )