char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
0329fcdac
char/aliceai-t5-moe
1# 2# GGUF file reading/modification support. For API usage information, 3# please see the files scripts/ for some fairly simple examples. 4# 5from __future__import annotations 6 7import logging 8import os 9import sys 10from collections import OrderedDict 11from typing import Any ,Literal ,NamedTuple ,TypeVar ,Union 12 13import numpy as np 14import numpy .typing as npt 15 16from .quants import quant_shape_to_byte_shape 17 18if __name__ == "__main__" : 19from pathlib import Path 20 21# Allow running file in package as a script. 22sys .path .insert (0 ,str (Path (__file__ ).parent .parent )) 23 24from gguf .constants import ( 25GGML_MAX_DIMS , 26GGML_QUANT_SIZES , 27GGUF_DEFAULT_ALIGNMENT , 28GGUF_MAGIC , 29GGUF_VERSION , 30GGMLQuantizationType , 31GGUFValueType , 32GGUFEndian , 33) 34 35# limits mirroring ggml/src/gguf.cpp (not part of gguf.h) 36GGUF_MAX_STRING_LENGTH = 1024 * 1024 * 1024 37GGUF_MAX_ARRAY_ELEMENTS = 1024 * 1024 * 1024 38 39logger = logging .getLogger (__name__ ) 40 41READER_SUPPORTED_VERSIONS = [2 ,GGUF_VERSION ] 42 43 44class ReaderField (NamedTuple ): 45# Offset to start of this field. 46offset :int 47 48# Name of the field (not necessarily from file data). 49name :str 50 51# Data parts. Some types have multiple components, such as strings 52# that consist of a length followed by the string data. 53parts :list [npt .NDArray [Any ]]= [] 54 55# Indexes into parts that we can call the actual data. For example 56# an array of strings will be populated with indexes to the actual 57# string data. 58data :list [int ]= [- 1 ] 59 60types :list [GGUFValueType ]= [] 61 62def contents (self ,index_or_slice :int | slice = slice (None ))-> Any : 63if self .types : 64to_string = lambda x :str (x .tobytes (),encoding = 'utf-8' )# noqa: E731 65main_type = self .types [0 ] 66 67if main_type == GGUFValueType .ARRAY : 68sub_type = self .types [- 1 ] 69 70if sub_type == GGUFValueType .STRING : 71indices = self .data [index_or_slice ] 72 73if isinstance (index_or_slice ,int ): 74return to_string (self .parts [indices ])# type: ignore 75else : 76return [to_string (self .parts [idx ])for idx in indices ]# type: ignore 77else : 78# FIXME: When/if _get_field_parts() support multi-dimensional arrays, this must do so too 79 80# Check if it's unsafe to perform slice optimization on data 81# if any(True for idx in self.data if len(self.parts[idx]) != 1): 82# optim_slice = slice(None) 83# else: 84# optim_slice = index_or_slice 85# index_or_slice = slice(None) 86 87# if isinstance(optim_slice, int): 88# return self.parts[self.data[optim_slice]].tolist()[0] 89# else: 90# return [pv for idx in self.data[optim_slice] for pv in self.parts[idx].tolist()][index_or_slice] 91 92if isinstance (index_or_slice ,int ): 93return self .parts [self .data [index_or_slice ]].tolist ()[0 ] 94else : 95return [pv for idx in self .data [index_or_slice ]for pv in self .parts [idx ].tolist ()] 96 97if main_type == GGUFValueType .STRING : 98return to_string (self .parts [- 1 ]) 99else : 100return self .parts [- 1 ].tolist ()[0 ] 101 102return None 103 104 105class ReaderTensor (NamedTuple ): 106name :str 107tensor_type :GGMLQuantizationType 108shape :npt .NDArray [np .uint32 ] 109n_elements :int 110n_bytes :int 111data_offset :int 112data :npt .NDArray [Any ] 113field :ReaderField 114 115 116class GGUFReader : 117# I - same as host, S - swapped 118byte_order :Literal ['I' ,'S' ]= 'I' 119alignment :int = GGUF_DEFAULT_ALIGNMENT 120data_offset :int 121 122# Note: Internal helper, API may change. 123gguf_scalar_to_np :dict [GGUFValueType ,type [np .generic ]]= { 124GGUFValueType .UINT8 :np .uint8 , 125GGUFValueType .INT8 :np .int8 , 126GGUFValueType .UINT16 :np .uint16 , 127GGUFValueType .INT16 :np .int16 , 128GGUFValueType .UINT32 :np .uint32 , 129GGUFValueType .INT32 :np .int32 , 130GGUFValueType .FLOAT32 :np .float32 , 131GGUFValueType .UINT64 :np .uint64 , 132GGUFValueType .INT64 :np .int64 , 133GGUFValueType .FLOAT64 :np .float64 , 134GGUFValueType .BOOL :np .bool_ , 135 } 136 137def __init__ (self ,path :os .PathLike [str ]| str ,mode :Literal ['r' ,'r+' ,'c' ]= 'r' ): 138self .data = np .memmap (path ,mode = mode ) 139offs = 0 140 141# Check for GGUF magic 142if self ._get (offs ,np .uint32 ,override_order = '<' )[0 ]!= GGUF_MAGIC : 143raise ValueError ('GGUF magic invalid' ) 144offs += 4 145 146# Check GGUF version 147temp_version = self ._get (offs ,np .uint32 ) 148if temp_version [0 ]& 65535 == 0 : 149# If we get 0 here that means it's (probably) a GGUF file created for 150# the opposite byte order of the machine this script is running on. 151self .byte_order = 'S' 152temp_version = temp_version .view (temp_version .dtype .newbyteorder (self .byte_order )) 153version = temp_version [0 ] 154if version not in READER_SUPPORTED_VERSIONS : 155raise ValueError (f'Sorry, file appears to be version { version } which we cannot handle' ) 156if sys .byteorder == "little" : 157# Host is little endian 158host_endian = GGUFEndian .LITTLE 159swapped_endian = GGUFEndian .BIG 160else : 161# Sorry PDP or other weird systems that don't use BE or LE. 162host_endian = GGUFEndian .BIG 163swapped_endian = GGUFEndian .LITTLE 164self .endianess = swapped_endian if self .byte_order == "S" else host_endian 165self .fields :OrderedDict [str ,ReaderField ]= OrderedDict () 166self .tensors :list [ReaderTensor ]= [] 167offs += self ._push_field (ReaderField (offs ,'GGUF.version' , [temp_version ], [0 ], [GGUFValueType .UINT32 ])) 168 169# Check tensor count and kv count 170temp_counts = self ._get (offs ,np .uint64 ,2 ) 171offs += self ._push_field (ReaderField (offs ,'GGUF.tensor_count' , [temp_counts [:1 ]], [0 ], [GGUFValueType .UINT64 ])) 172offs += self ._push_field (ReaderField (offs ,'GGUF.kv_count' , [temp_counts [1 :]], [0 ], [GGUFValueType .UINT64 ])) 173tensor_count ,kv_count = temp_counts 174if tensor_count > GGUF_MAX_ARRAY_ELEMENTS : 175raise ValueError (f'Tensor count { tensor_count } exceeds maximum { GGUF_MAX_ARRAY_ELEMENTS } ' ) 176if kv_count > GGUF_MAX_ARRAY_ELEMENTS : 177raise ValueError (f'KV count { kv_count } exceeds maximum { GGUF_MAX_ARRAY_ELEMENTS } ' ) 178offs = self ._build_fields (offs ,kv_count ) 179 180# Build Tensor Info Fields 181offs ,tensors_fields = self ._build_tensor_info (offs ,tensor_count ) 182new_align = self .fields .get ('general.alignment' ) 183if new_align is not None : 184if new_align .types != [GGUFValueType .UINT32 ]: 185raise ValueError ('Bad type for general.alignment field' ) 186self .alignment = new_align .parts [- 1 ][0 ] 187# Ensure alignment is a non-zero power of two 188if self .alignment == 0 or (self .alignment & (self .alignment - 1 ))!= 0 : 189raise ValueError ('Invalid alignment: must be a non-zero power of two' ) 190padding = offs % self .alignment 191if padding != 0 : 192offs += self .alignment - padding 193self .data_offset = offs 194self ._build_tensors (offs ,tensors_fields ) 195 196_DT = TypeVar ('_DT' ,bound = npt .DTypeLike ) 197 198# Fetch a key/value metadata field by key. 199def get_field (self ,key :str )-> Union [ReaderField ,None ]: 200return self .fields .get (key ,None ) 201 202# Fetch a tensor from the list by index. 203def get_tensor (self ,idx :int )-> ReaderTensor : 204return self .tensors [idx ] 205 206def _get ( 207self ,offset :int ,dtype :npt .DTypeLike ,count :int = 1 ,override_order :None | Literal ['I' ,'S' ,'<' ]= None , 208 )-> npt .NDArray [Any ]: 209count = int (count ) 210itemsize = int (np .empty ([],dtype = dtype ).itemsize ) 211end_offs = offset + itemsize * count 212arr = self .data [offset :end_offs ].view (dtype = dtype )[:count ] 213return arr .view (arr .dtype .newbyteorder (self .byte_order if override_order is None else override_order )) 214 215def _push_field (self ,field :ReaderField ,skip_sum :bool = False )-> int : 216if field .name in self .fields : 217# TODO: add option to make this a warning and accept duplicate keys like below 218raise KeyError (f'Duplicate { field . name } already in list at offset { field . offset } ' ) 219 220# logger.warning(f'Duplicate key {field.name} at offset {field.offset}') 221# self.fields[field.name + '_{}'.format(field.offset)] = field 222else : 223self .fields [field .name ]= field 224return 0 if skip_sum else sum (int (part .nbytes )for part in field .parts ) 225 226def _get_str (self ,offset :int )-> tuple [npt .NDArray [np .uint64 ],npt .NDArray [np .uint8 ]]: 227slen = self ._get (offset ,np .uint64 ) 228if int (slen [0 ])> GGUF_MAX_STRING_LENGTH : 229raise ValueError (f'String length { int ( slen [ 0 ]) } exceeds maximum { GGUF_MAX_STRING_LENGTH } ' ) 230if offset + 8 + int (slen [0 ])> self .data .nbytes : 231raise ValueError (f'String length { int ( slen [ 0 ]) } exceeds remaining file size { self . data . nbytes - offset - 8 } ' ) 232return slen ,self ._get (offset + 8 ,np .uint8 ,slen [0 ]) 233 234def _get_field_parts ( 235self ,orig_offs :int ,raw_type :int , 236 )-> tuple [int ,list [npt .NDArray [Any ]],list [int ],list [GGUFValueType ]]: 237offs = orig_offs 238types :list [GGUFValueType ]= [] 239gtype = GGUFValueType (raw_type ) 240types .append (gtype ) 241# Handle strings. 242if gtype == GGUFValueType .STRING : 243sparts :list [npt .NDArray [Any ]]= list (self ._get_str (offs )) 244size = sum (int (part .nbytes )for part in sparts ) 245return size ,sparts , [1 ],types 246# Check if it's a simple scalar type. 247nptype = self .gguf_scalar_to_np .get (gtype ) 248if nptype is not None : 249val = self ._get (offs ,nptype ) 250return int (val .nbytes ), [val ], [0 ],types 251# Handle arrays. 252if gtype == GGUFValueType .ARRAY : 253raw_itype = self ._get (offs ,np .uint32 ) 254offs += int (raw_itype .nbytes ) 255alen = self ._get (offs ,np .uint64 ) 256if int (alen [0 ])> GGUF_MAX_ARRAY_ELEMENTS : 257raise ValueError (f'Array length { int ( alen [ 0 ]) } exceeds maximum { GGUF_MAX_ARRAY_ELEMENTS } ' ) 258offs += int (alen .nbytes ) 259aparts :list [npt .NDArray [Any ]]= [raw_itype ,alen ] 260data_idxs :list [int ]= [] 261# FIXME: Handle multi-dimensional arrays properly instead of flattening 262for idx in range (alen [0 ]): 263curr_size ,curr_parts ,curr_idxs ,curr_types = self ._get_field_parts (offs ,raw_itype [0 ]) 264if idx == 0 : 265types += curr_types 266idxs_offs = len (aparts ) 267aparts += curr_parts 268data_idxs += (idx + idxs_offs for idx in curr_idxs ) 269offs += curr_size 270return offs - orig_offs ,aparts ,data_idxs ,types 271# We can't deal with this one. 272raise ValueError (f'Unknown/unhandled field type { gtype } ' ) 273 274def _get_tensor_info_field (self ,orig_offs :int )-> ReaderField : 275offs = orig_offs 276 277# Get Tensor Name 278name_len ,name_data = self ._get_str (offs ) 279offs += int (name_len .nbytes + name_data .nbytes ) 280 281# Get Tensor Dimensions Count 282n_dims = self ._get (offs ,np .uint32 ) 283offs += int (n_dims .nbytes ) 284if n_dims [0 ]> GGML_MAX_DIMS : 285raise ValueError (f'Tensor dimensions count { n_dims [ 0 ] } exceeds GGML_MAX_DIMS ( { GGML_MAX_DIMS } )' ) 286 287# Get Tensor Dimension Array 288dims = self ._get (offs ,np .uint64 ,n_dims [0 ]) 289offs += int (dims .nbytes ) 290 291# Get Tensor Encoding Scheme Type 292raw_dtype = self ._get (offs ,np .uint32 ) 293offs += int (raw_dtype .nbytes ) 294 295# Get Tensor Offset 296offset_tensor = self ._get (offs ,np .uint64 ) 297offs += int (offset_tensor .nbytes ) 298 299return ReaderField ( 300orig_offs , 301str (bytes (name_data ),encoding = 'utf-8' ), 302 [name_len ,name_data ,n_dims ,dims ,raw_dtype ,offset_tensor ], 303 [1 ,3 ,4 ,5 ], 304 ) 305 306def _build_fields (self ,offs :int ,count :int )-> int : 307for _ in range (count ): 308orig_offs = offs 309kv_klen ,kv_kdata = self ._get_str (offs ) 310offs += int (kv_klen .nbytes + kv_kdata .nbytes ) 311raw_kv_type = self ._get (offs ,np .uint32 ) 312offs += int (raw_kv_type .nbytes ) 313parts :list [npt .NDArray [Any ]]= [kv_klen ,kv_kdata ,raw_kv_type ] 314idxs_offs = len (parts ) 315field_size ,field_parts ,field_idxs ,field_types = self ._get_field_parts (offs ,raw_kv_type [0 ]) 316parts += field_parts 317self ._push_field (ReaderField ( 318orig_offs , 319str (bytes (kv_kdata ),encoding = 'utf-8' ), 320parts , 321 [idx + idxs_offs for idx in field_idxs ], 322field_types , 323 ),skip_sum = True ) 324offs += field_size 325return offs 326 327def _build_tensor_info (self ,offs :int ,count :int )-> tuple [int ,list [ReaderField ]]: 328tensor_fields = [] 329for _ in range (count ): 330field = self ._get_tensor_info_field (offs ) 331offs += sum (int (part .nbytes )for part in field .parts ) 332tensor_fields .append (field ) 333return offs ,tensor_fields 334 335def _build_tensors (self ,start_offs :int ,fields :list [ReaderField ])-> None : 336tensors = [] 337tensor_names = set ()# keep track of name to prevent duplicated tensors 338for field in fields : 339_name_len ,name_data ,_n_dims ,dims ,raw_dtype ,offset_tensor = field .parts 340# check if there's any tensor having same name already in the list 341tensor_name = str (bytes (name_data ),encoding = 'utf-8' ) 342if tensor_name in tensor_names : 343raise ValueError (f'Found duplicated tensor with name { tensor_name } ' ) 344tensor_names .add (tensor_name ) 345ggml_type = GGMLQuantizationType (raw_dtype [0 ]) 346# use Python ints: np.prod on uint64 wraps silently on overflow 347n_elems = 1 348for dim in dims .tolist (): 349n_elems *= int (dim ) 350np_dims = tuple (reversed (dims .tolist ())) 351block_size ,type_size = GGML_QUANT_SIZES [ggml_type ] 352n_bytes = n_elems * type_size // block_size 353data_offs = int (start_offs + offset_tensor [0 ]) 354item_type :npt .DTypeLike 355if ggml_type == GGMLQuantizationType .F16 : 356item_count = n_elems 357item_type = np .float16 358elif ggml_type == GGMLQuantizationType .F32 : 359item_count = n_elems 360item_type = np .float32 361elif ggml_type == GGMLQuantizationType .F64 : 362item_count = n_elems 363item_type = np .float64 364elif ggml_type == GGMLQuantizationType .I8 : 365item_count = n_elems 366item_type = np .int8 367elif ggml_type == GGMLQuantizationType .I16 : 368item_count = n_elems 369item_type = np .int16 370elif ggml_type == GGMLQuantizationType .I32 : 371item_count = n_elems 372item_type = np .int32 373elif ggml_type == GGMLQuantizationType .I64 : 374item_count = n_elems 375item_type = np .int64 376else : 377item_count = n_bytes 378item_type = np .uint8 379np_dims = quant_shape_to_byte_shape (np_dims ,ggml_type ) 380tensors .append (ReaderTensor ( 381name = tensor_name , 382tensor_type = ggml_type , 383shape = dims , 384n_elements = n_elems , 385n_bytes = n_bytes , 386data_offset = data_offs , 387data = self ._get (data_offs ,item_type ,item_count ).reshape (np_dims ), 388field = field , 389 )) 390self .tensors = tensors