Skip to content

Commit 8219cfe

Browse files
committed
refactor(convert): route SafeTensors conversion through Python fallback
Use the official llama.cpp Python converter as the single conversion path so runtime behavior is consistent across architectures and users get clear dependency installation errors when Python env is missing. Made-with: Cursor
1 parent 5fafced commit 8219cfe

File tree

1 file changed

+3
-77
lines changed

1 file changed

+3
-77
lines changed

internal/convert/convert.go

Lines changed: 3 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package convert
22

33
import (
44
"encoding/binary"
5-
"fmt"
65
"math"
76
"os"
87
"path/filepath"
@@ -13,87 +12,14 @@ import (
1312
type ProgressFunc func(step string, current, total int)
1413

1514
// Convert converts SafeTensors model files in modelDir to a GGUF file.
16-
// Each architecture has a dedicated Go converter. For architectures without
17-
// a Go converter, it falls back to the official llama.cpp convert_hf_to_gguf.py.
15+
// Conversion is delegated to the official llama.cpp convert_hf_to_gguf.py
16+
// so users only need one consistent conversion path.
1817
func Convert(modelDir string, progress ProgressFunc) (string, error) {
1918
if progress == nil {
2019
progress = func(string, int, int) {}
2120
}
2221

23-
progress("Reading model configuration", 0, 0)
24-
25-
cfg, err := loadModelConfig(modelDir)
26-
if err != nil {
27-
return "", fmt.Errorf("loading config: %w", err)
28-
}
29-
30-
hfArch := cfg.Architectures[0]
31-
ggufArch, ok := detectGGUFArch(hfArch)
32-
if !ok {
33-
return ConvertPython(modelDir, progress)
34-
}
35-
36-
converter := getConverter(ggufArch, cfg)
37-
if converter == nil {
38-
return ConvertPython(modelDir, progress)
39-
}
40-
41-
progress("Scanning SafeTensors files", 0, 0)
42-
43-
stFiles, err := scanSafeTensorsFiles(modelDir)
44-
if err != nil {
45-
return "", fmt.Errorf("scanning SafeTensors: %w", err)
46-
}
47-
48-
sources := collectTensors(stFiles)
49-
50-
progress("Parsing tokenizer", 0, 0)
51-
52-
tok, err := parseTokenizer(modelDir, hfArch)
53-
if err != nil {
54-
return "", fmt.Errorf("parsing tokenizer: %w", err)
55-
}
56-
57-
if cfg.VocabSize > len(tok.Tokens) {
58-
for i := len(tok.Tokens); i < cfg.VocabSize; i++ {
59-
tok.Tokens = append(tok.Tokens, fmt.Sprintf("[PAD%d]", i))
60-
tok.Scores = append(tok.Scores, -1)
61-
tok.Types = append(tok.Types, tokenTypeUserDefined)
62-
}
63-
}
64-
65-
progress("Building GGUF", 0, 0)
66-
67-
writer := newGGUFWriter()
68-
converter.WriteKV(writer, cfg)
69-
writeTokenizerKV(writer, tok, cfg)
70-
71-
if err := converter.ConvertTensors(writer, sources, cfg, progress); err != nil {
72-
return "", fmt.Errorf("converting tensors: %w", err)
73-
}
74-
75-
idx := findKVIndex(writer.kvs, "general.file_type")
76-
if idx >= 0 {
77-
writer.kvs[idx].value = uint32(1) // GGML_FTYPE_MOSTLY_F16
78-
}
79-
80-
outputName := generateOutputName(modelDir, cfg)
81-
outputPath := filepath.Join(modelDir, outputName)
82-
tmpPath := outputPath + ".tmp"
83-
84-
progress("Writing GGUF file", 0, 0)
85-
86-
if err := writer.writeTo(tmpPath); err != nil {
87-
os.Remove(tmpPath)
88-
return "", fmt.Errorf("writing GGUF: %w", err)
89-
}
90-
91-
if err := os.Rename(tmpPath, outputPath); err != nil {
92-
os.Remove(tmpPath)
93-
return "", fmt.Errorf("finalizing GGUF: %w", err)
94-
}
95-
96-
return outputPath, nil
22+
return ConvertPython(modelDir, progress)
9723
}
9824

9925
// HasGGUF checks if a GGUF file already exists in the model directory.

0 commit comments

Comments
 (0)