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 ,gguf 11 12 13@ ModelBase . register ( "KimiVLForConditionalGeneration" ) 14@ ModelBase . example ( "moonshotai/Kimi-VL-A3B-Instruct" ) 15class KimiVLModel (MmprojModel ): 16def __init__ (self ,* args ,** kwargs ): 17super ().__init__ (* args ,** kwargs ) 18assert self .hparams_vision is not None 19self .hparams_vision ["image_size" ]= 64 * 14 # for compatibility 20 21def set_gguf_parameters (self ): 22super ().set_gguf_parameters () 23self .gguf_writer .add_clip_projector_type (gguf .VisionProjectorType .KIMIVL ) 24self .gguf_writer .add_vision_use_gelu (True ) 25self .gguf_writer .add_vision_projector_scale_factor (2 ) 26# eps is the same as pytorch's default value 27assert self .hparams_vision is not None 28self .gguf_writer .add_vision_attention_layernorm_eps (self .hparams_vision .get ("layer_norm_eps" ,1e-5 )) 29 30@classmethod 31def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 32name ,gen = item 33 34is_vision_tensor = "vision_tower" in name or "multi_modal_projector" in name 35 36if not is_vision_tensor : 37return None 38 39return super ().filter_tensors (item ) 40 41def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 42if "pos_emb.weight" in name : 43data_torch = data_torch .view (data_torch .shape [0 ]* data_torch .shape [1 ],data_torch .shape [2 ]) 44 45if "wqkv" in name : 46split_dim = 0 if "weight" in name else - 1 47wq ,wk ,wv = data_torch .chunk (3 ,dim = split_dim ) 48yield from super ().modify_tensors (wq ,name .replace ("wqkv" ,"wq" ),bid ) 49yield from super ().modify_tensors (wk ,name .replace ("wqkv" ,"wk" ),bid ) 50yield from super ().modify_tensors (wv ,name .replace ("wqkv" ,"wv" ),bid ) 51else : 52yield from super ().modify_tensors (data_torch ,name ,bid ) 53 54 55@ ModelBase . register ( "KimiK25ForConditionalGeneration" ) 56@ ModelBase . example ( "moonshotai/Kimi-K2.5" ) 57class KimiK25Model (MmprojModel ): 58"""Kimi-K2.5 with MoonViT3d vision encoder""" 59 60def __init__ (self ,* args ,** kwargs ): 61super ().__init__ (* args ,** kwargs ) 62 63assert self .hparams_vision is not None ,"Kimi-K2.5 requires vision_config in model config" 64 65self .merge_kernel_size = tuple (self .hparams_vision .get ("merge_kernel_size" , [2 ,2 ])) 66self .patch_size = self .hparams_vision .get ("patch_size" ,14 ) 67 68# Set image_size for compatibility with base class 69# Use position embedding dimensions as image_size reference 70pos_emb_h = self .hparams_vision .get ("init_pos_emb_height" ,64 ) 71self .hparams_vision ["image_size" ]= pos_emb_h * self .patch_size 72 73def set_gguf_parameters (self ): 74# Base class MmprojModel.set_gguf_parameters() already writes: 75# - vision_block_count, vision_head_count, vision_embedding_length 76# - vision_feed_forward_length, vision_patch_size, image_mean, image_std 77# via find_vparam() which handles the vt_* prefixed keys in Kimi-K2.5's config 78super ().set_gguf_parameters () 79assert self .hparams_vision is not None 80 81self .gguf_writer .add_clip_projector_type (gguf .VisionProjectorType .KIMIK25 ) 82 83# Position embedding parameters (for interpolation) 84self .gguf_writer .add_uint32 ("vision.pos_emb_height" ,self .hparams_vision .get ("init_pos_emb_height" ,64 )) 85self .gguf_writer .add_uint32 ("vision.pos_emb_width" ,self .hparams_vision .get ("init_pos_emb_width" ,64 )) 86self .gguf_writer .add_uint32 ("vision.pos_emb_time" ,self .hparams_vision .get ("init_pos_emb_time" ,4 )) 87 88# Projector parameters 89self .gguf_writer .add_vision_use_gelu (self .hparams_vision .get ("projector_hidden_act" ,"gelu" )== "gelu" ) 90self .gguf_writer .add_vision_attention_layernorm_eps (self .hparams_vision .get ("projector_ln_eps" ,1e-5 )) 91self .gguf_writer .add_vision_projector_scale_factor (self .merge_kernel_size [0 ]) 92 93# Image size limits 94# Note: in_patch_limit is for images, in_patch_limit_each_frame is for video (not supported yet) 95in_patch_limit = self .preprocessor_config .get ("in_patch_limit" ,16384 ) 96min_patches = 8 # reasonable minimum 97pixels_per_patch = self .patch_size ** 2 98self .gguf_writer .add_vision_min_pixels (min_patches * pixels_per_patch ) 99self .gguf_writer .add_vision_max_pixels (in_patch_limit * pixels_per_patch ) 100 101@staticmethod 102def permute (weights :Tensor ,n_head :int )-> Tensor : 103out_dim ,in_dim = weights .shape 104head_dim = out_dim // n_head 105w = weights .reshape (n_head ,head_dim // 4 ,2 ,2 ,in_dim ) 106w = w .permute (0 ,2 ,1 ,3 ,4 ) 107return w .reshape (out_dim ,in_dim ) 108 109@classmethod 110def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 111name ,gen = item 112 113# Only process vision and projector tensors 114is_vision = any (x in name for x in ["vision_tower" ,"mm_projector" ]) 115 116if not is_vision : 117return None 118 119return super ().filter_tensors (item ) 120 121def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 122assert self .hparams_vision is not None 123n_head = self .hparams_vision .get ("num_attention_heads" ,16 ) 124 125# Permute Q/K weights/biases from interleaved to split RoPE format 126# This allows using build_rope_2d at runtime without post-permutation. 127if "wqkv" in name : 128out_dim = data_torch .shape [0 ] 129qkv_dim = out_dim // 3 130head_dim = qkv_dim // n_head 131 132if "weight" in name : 133wq ,wk ,wv = data_torch [:qkv_dim , :],data_torch [qkv_dim :2 * qkv_dim , :],data_torch [2 * qkv_dim :, :] 134wq = self .permute (wq ,n_head ) 135wk = self .permute (wk ,n_head ) 136data_torch = torch .cat ([wq ,wk ,wv ],dim = 0 ) 137elif "bias" in name : 138bq ,bk ,bv = data_torch [:qkv_dim ],data_torch [qkv_dim :2 * qkv_dim ],data_torch [2 * qkv_dim :] 139bq = bq .reshape (n_head ,head_dim // 4 ,2 ,2 ).permute (0 ,2 ,1 ,3 ).reshape (- 1 ) 140bk = bk .reshape (n_head ,head_dim // 4 ,2 ,2 ).permute (0 ,2 ,1 ,3 ).reshape (- 1 ) 141data_torch = torch .cat ([bq ,bk ,bv ],dim = 0 ) 142 143# Temporal embeddings: (T, 1, C) → (T, C) 144if "pos_emb.time_weight" in name : 145T ,_ ,C = data_torch .shape 146data_torch = data_torch .reshape (T ,C ) 147 148# PatchMergerMLP tensor name mapping 149# proj.0.weight → proj.linear_1.weight 150# proj.2.weight → proj.linear_2.weight 151if "mm_projector.proj.0." in name : 152name = name .replace (".proj.0." ,".proj.linear_1." ) 153elif "mm_projector.proj.2." in name : 154name = name .replace (".proj.2." ,".proj.linear_2." ) 155 156yield from super ().modify_tensors (data_torch ,name ,bid ) 157 158 159@ ModelBase . register ( "Glm5vForConditionalGeneration" ) 160# [TAG_HF_EXAMPLE_MISSING] 161class Glm5vModel (KimiK25Model ): 162"""GLM-5.2-Vision MoonViT3d encoder and projector 163 164Uses the same vision encoder and projector as Kimi-K2.5, so it reuses the 165kimik25 projector type. The image begin/end tokens differ, but they are 166resolved at runtime from the text model vocab. 167""" 168 169def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 170if name .startswith ("mm_projector.linear_" ): 171name = name .replace ("mm_projector.linear_" ,"mm_projector.proj.linear_" ,1 ) 172 173yield from super ().modify_tensors (data_torch ,name ,bid )