@@ -636,18 +636,16 @@ def get_type(val):
636
636
sys .exit ()
637
637
638
638
639
+ class WriterState :
640
+ EMPTY = auto ()
641
+ HEADER = auto ()
642
+ KV_DATA = auto ()
643
+ TI_DATA = auto ()
644
+
645
+
639
646
class GGUFWriter :
640
647
fout : BufferedWriter
641
- arch : str
642
- offset_tensor = 0
643
- data_alignment = GGUF_DEFAULT_ALIGNMENT
644
- kv_data = b""
645
- kv_data_count = 0
646
- ti_data = b""
647
- ti_data_count = 0
648
- use_temp_file : bool
649
- temp_file : tempfile .SpooledTemporaryFile [bytes ] | None = None
650
- tensors : list [tuple [np .ndarray [Any , Any ], int ]]
648
+ tensors : list [np .ndarray [Any , Any ]]
651
649
652
650
@property
653
651
def pack_prefix (self ):
@@ -656,9 +654,15 @@ def pack_prefix(self):
656
654
else :
657
655
return ">"
658
656
659
- def __init__ (self , path : os .PathLike [str ] | str , arch : str , use_temp_file = True , endianess = GGUFEndian .LITTLE ):
657
+ def __init__ (self , path : os .PathLike [str ] | str , arch : str , endianess = GGUFEndian .LITTLE ):
660
658
self .fout = open (path , "wb" )
661
659
self .arch = arch
660
+ self .offset_tensor = 0
661
+ self .data_alignment = GGUF_DEFAULT_ALIGNMENT
662
+ self .kv_data = b""
663
+ self .kv_data_count = 0
664
+ self .ti_data = b""
665
+ self .ti_data_count = 0
662
666
self .endianess = endianess
663
667
self ._simple_value_packing = {
664
668
GGUFValueType .UINT8 : f"{ self .pack_prefix } B" ,
@@ -673,27 +677,41 @@ def __init__(self, path: os.PathLike[str] | str, arch: str, use_temp_file = True
673
677
GGUFValueType .FLOAT64 : f"{ self .pack_prefix } d" ,
674
678
GGUFValueType .BOOL : "?" ,
675
679
}
676
- self .add_architecture ()
677
- self .use_temp_file = use_temp_file
678
680
self .tensors = []
681
+ self .state = WriterState .EMPTY
682
+
683
+ self .add_architecture ()
684
+
679
685
endianess_str = "Big Endian" if self .endianess == GGUFEndian .BIG else "Little Endian"
680
686
print (f"This gguf file is for { endianess_str } only" )
681
687
682
688
def write_header_to_file (self ):
689
+ if self .state is not WriterState .EMPTY :
690
+ raise ValueError (f'Expected output file to be empty, got { self .state } ' )
691
+
683
692
self .fout .write (struct .pack ("<I" , GGUF_MAGIC ))
684
693
self .fout .write (struct .pack (f"{ self .pack_prefix } I" , GGUF_VERSION ))
685
694
self .fout .write (struct .pack (f"{ self .pack_prefix } Q" , self .ti_data_count ))
686
695
self .fout .write (struct .pack (f"{ self .pack_prefix } Q" , self .kv_data_count ))
687
696
self .flush ()
688
- # print("tensors " + str(self.ti_data_count) + " kv " + str(self.kv_data_count))
697
+ #print("tensors " + str(self.ti_data_count) + " kv " + str(self.kv_data_count))
698
+ self .state = WriterState .HEADER
689
699
690
700
def write_kv_data_to_file (self ):
701
+ if self .state is not WriterState .HEADER :
702
+ raise ValueError (f'Expected output file to contain the header, got { self .state } ' )
703
+
691
704
self .fout .write (self .kv_data )
692
705
self .flush ()
706
+ self .state = WriterState .KV_DATA
693
707
694
708
def write_ti_data_to_file (self ):
709
+ if self .state is not WriterState .KV_DATA :
710
+ raise ValueError (f'Expected output file to contain KV data, got { self .state } ' )
711
+
695
712
self .fout .write (self .ti_data )
696
713
self .flush ()
714
+ self .state = WriterState .TI_DATA
697
715
698
716
def add_key (self , key : str ):
699
717
self .add_val (key , GGUFValueType .STRING , add_vtype = False )
@@ -807,33 +825,24 @@ def add_tensor_info(self, name: str, tensor_shape: Sequence[int], tensor_dtype:
807
825
def add_tensor (self , name : str , tensor : np .ndarray [Any , Any ], raw_shape : Sequence [int ] | None = None , raw_dtype : GGMLQuantizationType | None = None ):
808
826
if self .endianess == GGUFEndian .BIG :
809
827
tensor .byteswap (inplace = True )
810
- if self .use_temp_file and self .temp_file is None :
811
- fp = tempfile .SpooledTemporaryFile (mode = "w+b" , max_size = 256 * 1024 * 1024 )
812
- fp .seek (0 )
813
- self .temp_file = fp
814
828
815
829
shape : Sequence [int ] = raw_shape if raw_shape is not None else tensor .shape
816
830
self .add_tensor_info (name , shape , tensor .dtype , tensor .nbytes , raw_dtype = raw_dtype )
817
831
818
- pad = GGUFWriter .ggml_pad (tensor .nbytes , self .data_alignment ) - tensor .nbytes
819
-
820
- if self .temp_file is None :
821
- self .tensors .append ((tensor , pad ))
822
- return
823
-
824
- tensor .tofile (self .temp_file )
825
-
826
- if pad != 0 :
827
- self .temp_file .write (bytes ([0 ] * pad ))
832
+ self .tensors .append (tensor )
828
833
829
834
def write_padding (self , fp : BinaryIO , n : int , align : int | None = None ):
830
835
pad = GGUFWriter .ggml_pad (n , align if align is not None else self .data_alignment ) - n
831
836
if pad != 0 :
832
837
fp .write (bytes ([0 ] * pad ))
833
838
834
839
def write_tensor_data (self , tensor : np .ndarray [Any , Any ]):
840
+ if self .state is not WriterState .TI_DATA :
841
+ raise ValueError (f'Expected output file to contain tensor info, got { self .state } ' )
842
+
835
843
if self .endianess == GGUFEndian .BIG :
836
844
tensor .byteswap (inplace = True )
845
+
837
846
self .write_padding (self .fout , self .fout .tell ())
838
847
tensor .tofile (self .fout )
839
848
self .write_padding (self .fout , tensor .nbytes )
@@ -843,18 +852,13 @@ def write_tensors_to_file(self):
843
852
844
853
self .write_padding (self .fout , self .fout .tell ())
845
854
846
- if self .temp_file is None :
847
- for (currtensor , currpad ) in self .tensors :
848
- currtensor .tofile (self .fout )
849
- if currpad != 0 :
850
- self .fout .write (bytes ([0 ] * currpad ))
851
- return
852
-
853
- self .temp_file .seek (0 )
854
-
855
- shutil .copyfileobj (self .temp_file , self .fout )
856
- self .flush ()
857
- self .temp_file .close ()
855
+ while True :
856
+ try :
857
+ tensor = self .tensors .pop (0 )
858
+ except IndexError :
859
+ break
860
+ tensor .tofile (self .fout )
861
+ self .write_padding (self .fout , tensor .nbytes )
858
862
859
863
def flush (self ):
860
864
self .fout .flush ()
@@ -983,11 +987,11 @@ def add_pad_token_id(self, id: int):
983
987
984
988
985
989
class SpecialVocab :
986
- load_merges : bool = False
987
- merges : list [str ] = []
988
- special_token_types : tuple [str , ...] = ( 'bos' , 'eos' , 'unk' , 'sep' , 'pad' )
989
- special_token_ids : dict [str , int ] = {}
990
- n_vocab : int | None = None
990
+ load_merges : bool
991
+ merges : list [str ]
992
+ special_token_types : tuple [str , ...]
993
+ special_token_ids : dict [str , int ]
994
+ n_vocab : int | None
991
995
992
996
def __init__ (
993
997
self , path : str | os .PathLike [str ], load_merges : bool = False ,
@@ -997,8 +1001,11 @@ def __init__(
997
1001
self .special_token_ids = {}
998
1002
self .n_vocab = n_vocab
999
1003
self .load_merges = load_merges
1004
+ self .merges = []
1000
1005
if special_token_types is not None :
1001
1006
self .special_token_types = special_token_types
1007
+ else :
1008
+ self .special_token_types = ('bos' , 'eos' , 'unk' , 'sep' , 'pad' )
1002
1009
self ._load (Path (path ))
1003
1010
1004
1011
def _load (self , path : Path ) -> None :
0 commit comments