char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
465e49b9c
char/aliceai-t5-moe
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4from __future__import annotations 5 6import argparse 7import logging 8import os 9import sys 10from pathlib import Path 11 12import torch 13 14if 'NO_LOCAL_GGUF' not in os .environ : 15sys .path .insert (1 ,str (Path (__file__ ).parent / 'gguf-py' )) 16import gguf 17 18from conversion import ( 19ModelBase , 20ModelType , 21get_model_architecture , 22get_model_class , 23logger , 24print_registered_models , 25_mistral_common_installed , 26_mistral_import_error_msg , 27) 28 29 30def split_str_to_n_bytes (split_str :str )-> int : 31if split_str .endswith ("K" ): 32n = int (split_str [:- 1 ])* 1000 33elif split_str .endswith ("M" ): 34n = int (split_str [:- 1 ])* 1000 * 1000 35elif split_str .endswith ("G" ): 36n = int (split_str [:- 1 ])* 1000 * 1000 * 1000 37elif split_str .isnumeric (): 38n = int (split_str ) 39else : 40raise ValueError (f"Invalid split size: { split_str } , must be a number, optionally followed by K, M, or G" ) 41 42if n < 0 : 43raise ValueError (f"Invalid split size: { split_str } , must be positive" ) 44 45return n 46 47 48def parse_args ()-> argparse .Namespace : 49parser = argparse .ArgumentParser ( 50description = "Convert a huggingface model to a GGML compatible file" ) 51parser .add_argument ( 52"--vocab-only" ,action = "store_true" , 53help = "extract only the vocab" , 54 ) 55parser .add_argument ( 56"--outfile" ,type = Path , 57help = "path to write to; default: based on input. {ftype} will be replaced by the outtype." , 58 ) 59parser .add_argument ( 60"--outtype" ,type = str ,choices = ["f32" ,"f16" ,"bf16" ,"q8_0" ,"tq1_0" ,"tq2_0" ,"auto" ],default = "auto" , 61help = "output format - use f32 for float32, f16 for float16, bf16 for bfloat16, q8_0 for Q8_0, tq1_0 or tq2_0 for ternary, and auto for the highest-fidelity 16-bit float type" , 62 ) 63parser .add_argument ( 64"--bigendian" ,action = "store_true" , 65help = "model is executed on big endian machine" , 66 ) 67parser .add_argument ( 68"model" ,type = str , 69help = "directory containing model file or huggingface repository ID (if --remote)" , 70nargs = "?" , 71 ) 72parser .add_argument ( 73"--use-temp-file" ,action = "store_true" , 74help = "use the tempfile library while processing (helpful when running out of memory, process killed)" , 75 ) 76parser .add_argument ( 77"--no-lazy" ,action = "store_true" , 78help = "use more RAM by computing all outputs before writing (use in case lazy evaluation is broken)" , 79 ) 80parser .add_argument ( 81"--model-name" ,type = str ,default = None , 82help = "name of the model" , 83 ) 84parser .add_argument ( 85"--verbose" ,action = "store_true" , 86help = "increase output verbosity" , 87 ) 88parser .add_argument ( 89"--split-max-tensors" ,type = int ,default = 0 , 90help = "max tensors in each split" , 91 ) 92parser .add_argument ( 93"--split-max-size" ,type = str ,default = "0" , 94help = "max size per split N(M|G)" , 95 ) 96parser .add_argument ( 97"--dry-run" ,action = "store_true" , 98help = "only print out a split plan and exit, without writing any new files" , 99 ) 100parser .add_argument ( 101"--no-tensor-first-split" ,action = "store_true" , 102help = "do not add tensors to the first split (disabled by default)" 103 ) 104parser .add_argument ( 105"--metadata" ,type = Path , 106help = "Specify the path for an authorship metadata override file" 107 ) 108parser .add_argument ( 109"--print-supported-models" ,action = "store_true" , 110help = "Print the supported models" 111 ) 112parser .add_argument ( 113"--remote" ,action = "store_true" , 114help = "(Experimental) Read safetensors file remotely without downloading to disk. Config and tokenizer files will still be downloaded. To use this feature, you need to specify Hugging Face model repo name instead of a local directory. For example: 'HuggingFaceTB/SmolLM2-1.7B-Instruct'. Note: To access gated repo, set HF_TOKEN environment variable to your Hugging Face token." , 115 ) 116parser .add_argument ( 117"--mmproj" ,action = "store_true" , 118help = "Export multimodal projector (mmproj) for vision models. This will only work on some vision models. An 'mmproj-' prefix will be added to the output file name." , 119 ) 120parser .add_argument ( 121"--mtp" ,action = "store_true" , 122help = "Export only the multi-token prediction (MTP) head as a separate GGUF, suitable for use as a speculative draft. An 'mtp-' prefix will be added to the output file name." , 123 ) 124parser .add_argument ( 125"--no-nextn" ,"--no-mtp" ,dest = "no_mtp" ,action = "store_true" , 126help = "Exclude NextN speculative draft tensors from the converted GGUF. Pair with --mtp or --dspark on a second run to publish target and draft as two files." , 127 ) 128parser .add_argument ( 129"--dspark" ,action = "store_true" , 130help = "Export only the DeepSeek-V4 DSpark draft tensors as a separate GGUF." , 131 ) 132parser .add_argument ( 133"--mistral-format" ,action = "store_true" , 134help = "Whether the model is stored following the Mistral format." , 135 ) 136parser .add_argument ( 137"--disable-mistral-community-chat-template" ,action = "store_true" , 138help = ( 139"Whether to disable usage of Mistral community chat templates. If set, use the Mistral official `mistral-common` library for tokenization and detokenization of Mistral models. " 140"Using `mistral-common` ensure correctness and zero-day support of tokenization for models converted from the Mistral format but requires to manually setup the tokenization server." 141 ) 142 ) 143 144parser .add_argument ( 145"--sentence-transformers-dense-modules" ,action = "store_true" , 146help = ("Whether to include sentence-transformers dense modules. " 147"It can be used for sentence-transformers models, like google/embeddinggemma-300m. " 148"Default these modules are not included." ) 149 ) 150 151parser .add_argument ( 152"--fuse-gate-up-exps" ,action = "store_true" , 153help = "Fuse gate_exps and up_exps tensors into a single gate_up_exps tensor for MoE models." , 154 ) 155parser .add_argument ( 156"--fp8-as-q8" ,action = "store_true" , 157help = "Store tensors dequantized from FP8 as Q8_0 instead of BF16/F16." , 158 ) 159 160parser .add_argument ( 161"--fuse-qkv" ,action = "store_true" , 162help = "Fuse separate Q, K, V weight tensors into a single QKV tensor." , 163 ) 164parser .add_argument ( 165"--target-model-dir" ,type = str ,default = None , 166help = ( 167"path to the target model directory; required when converting a standalone draft model " 168"(e.g. EAGLE3 / DFlash) that needs target-model metadata such as tokenizer, hidden size, and " 169"layer count to populate its GGUF." 170 ), 171 ) 172 173args = parser .parse_args () 174if not args .print_supported_models and args .model is None : 175parser .error ("the following arguments are required: model" ) 176return args 177 178 179def main ()-> None : 180args = parse_args () 181 182if args .print_supported_models : 183logger .error ("Supported models:" ) 184print_registered_models () 185sys .exit (0 ) 186 187if args .verbose : 188logging .basicConfig (level = logging .DEBUG ) 189else : 190logging .basicConfig (level = logging .INFO ) 191 192if args .remote : 193hf_repo_id = args .model 194from huggingface_hub import snapshot_download 195allowed_patterns = ["LICENSE" ,"*.json" ,"*.md" ,"*.txt" ,"tokenizer.model" ] 196if args .sentence_transformers_dense_modules : 197# include sentence-transformers dense modules safetensors files 198allowed_patterns .append ("*.safetensors" ) 199local_dir = snapshot_download ( 200repo_id = hf_repo_id , 201allow_patterns = allowed_patterns ) 202dir_model = Path (local_dir ) 203logger .info (f"Downloaded config and tokenizer to { local_dir } " ) 204else : 205hf_repo_id = None 206dir_model = Path (args .model ) 207 208if not dir_model .is_dir (): 209logger .error (f'Error: { dir_model } is not a directory' ) 210sys .exit (1 ) 211 212ftype_map :dict [str ,gguf .LlamaFileType ]= { 213"f32" :gguf .LlamaFileType .ALL_F32 , 214"f16" :gguf .LlamaFileType .MOSTLY_F16 , 215"bf16" :gguf .LlamaFileType .MOSTLY_BF16 , 216"q8_0" :gguf .LlamaFileType .MOSTLY_Q8_0 , 217"tq1_0" :gguf .LlamaFileType .MOSTLY_TQ1_0 , 218"tq2_0" :gguf .LlamaFileType .MOSTLY_TQ2_0 , 219"auto" :gguf .LlamaFileType .GUESSED , 220 } 221 222is_split = args .split_max_tensors > 0 or args .split_max_size != "0" 223if args .use_temp_file and is_split : 224logger .error ("Error: Cannot use temp file when splitting" ) 225sys .exit (1 ) 226 227if args .outfile is not None : 228fname_out = args .outfile 229elif hf_repo_id : 230# if remote, use the model ID as the output file name 231fname_out = Path ("./" + hf_repo_id .replace ("/" ,"-" )+ "-{ftype}.gguf" ) 232else : 233fname_out = dir_model 234 235logger .info (f"Loading model: { dir_model . name } " ) 236 237is_mistral_format = args .mistral_format 238if is_mistral_format and not _mistral_common_installed : 239raise ImportError (_mistral_import_error_msg ) 240disable_mistral_community_chat_template = args .disable_mistral_community_chat_template 241 242with torch .inference_mode (): 243output_type = ftype_map [args .outtype ] 244model_type = ModelType .MMPROJ if args .mmproj else ModelType .TEXT 245hparams = ModelBase .load_hparams (dir_model ,is_mistral_format ) 246if not is_mistral_format : 247model_architecture = get_model_architecture (hparams ,model_type ) 248logger .info (f"Model architecture: { model_architecture } " ) 249try : 250model_class = get_model_class (model_architecture ,mmproj = (model_type == ModelType .MMPROJ )) 251except NotImplementedError : 252logger .error (f"Model { model_architecture } is not supported" ) 253sys .exit (1 ) 254elif args .mmproj : 255assert hparams .get ("vision_encoder" )is not None ,"This model does not support multimodal" 256from conversion .pixtral import PixtralModel 257model_class = PixtralModel 258elif hparams .get ("moe" )is not None : 259from conversion .mistral import MistralMoeModel 260model_class = MistralMoeModel 261else : 262from conversion .mistral import MistralModel 263model_class = MistralModel 264 265if sum ((args .mtp ,args .no_mtp ,args .dspark ))> 1 : 266logger .error ("--mtp, --no-nextn, and --dspark are mutually exclusive" ) 267sys .exit (1 ) 268 269if args .dspark : 270if is_mistral_format or model_architecture != "DeepseekV4ForCausalLM" : 271logger .error ("--dspark is only supported for DeepseekV4ForCausalLM" ) 272sys .exit (1 ) 273from conversion .deepseek import DeepseekV4DSparkModel 274model_class = DeepseekV4DSparkModel 275 276if args .mtp or args .no_mtp : 277if not model_class .supports_mtp_export : 278logger .error ("--mtp / --no-nextn are not supported for %s" ,model_architecture ) 279sys .exit (1 ) 280if args .no_mtp : 281model_class .no_mtp = True 282if args .mtp : 283model_class .mtp_only = True 284 285model_instance = model_class (dir_model ,output_type ,fname_out , 286is_big_endian = args .bigendian ,use_temp_file = args .use_temp_file , 287eager = args .no_lazy , 288metadata_override = args .metadata ,model_name = args .model_name , 289split_max_tensors = args .split_max_tensors , 290split_max_size = split_str_to_n_bytes (args .split_max_size ),dry_run = args .dry_run , 291small_first_shard = args .no_tensor_first_split , 292remote_hf_model_id = hf_repo_id ,disable_mistral_community_chat_template = disable_mistral_community_chat_template , 293sentence_transformers_dense_modules = args .sentence_transformers_dense_modules , 294target_model_dir = Path (args .target_model_dir )if args .target_model_dir else None , 295fuse_gate_up_exps = args .fuse_gate_up_exps , 296fp8_as_q8 = args .fp8_as_q8 , 297fuse_qkv = args .fuse_qkv , 298 ) 299 300if args .vocab_only : 301logger .info ("Exporting model vocab..." ) 302model_instance .write_vocab () 303logger .info (f"Model vocab successfully exported to { model_instance . fname_out } " ) 304else : 305logger .info ("Exporting model..." ) 306model_instance .write () 307out_path = f" { model_instance . fname_out . parent } { os . sep } " if is_split else model_instance .fname_out 308logger .info (f"Model successfully exported to { out_path } " ) 309 310 311if __name__ == '__main__' : 312main ()