main.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. // Command convert converts HuggingFace models (safetensors) to .mak format
  2. //
  3. // Pure Go implementation - no Python dependencies!
  4. //
  5. // Usage:
  6. //
  7. // convert /path/to/model output.mak
  8. // convert /path/to/model output.mak --quant q4_k
  9. // convert /path/to/model output.mak --quant q4_k --mix
  10. package main
  11. import (
  12. "flag"
  13. "fmt"
  14. "os"
  15. "strings"
  16. "makarna/pkg/convert"
  17. _ "makarna/pkg/model/models"
  18. "makarna/pkg/quant"
  19. )
  20. func main() {
  21. fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
  22. quantType := fs.String("quant", "", "Quantization type: q2_k, q3_k, q4_k, q5_k, q6_k, q8_k (empty = F32)")
  23. mixMode := fs.Bool("mix", false, "Enable smart mix quantization")
  24. workers := fs.Int("workers", 0, "Number of parallel workers for conversion (0 = GOMAXPROCS)")
  25. maxInFlightMB := fs.Int("max-inflight-mb", 0, "Max in-flight tensor memory (decoded+output) in MB (0 = auto)")
  26. // Go's standard flag package stops parsing at the first non-flag argument.
  27. // Users often pass: convert <model_dir> <out.mak> --quant q4_k --mix
  28. // Pre-scan to allow flags to appear anywhere.
  29. flagArgs, posArgs := splitFlagsAndPositionals(os.Args[1:])
  30. _ = fs.Parse(flagArgs)
  31. args := posArgs
  32. if len(args) < 2 {
  33. fmt.Println("Usage: convert <model_dir> <output.mak> [--quant TYPE] [--mix]")
  34. os.Exit(1)
  35. }
  36. modelPath := args[0]
  37. outputPath := args[1]
  38. var baseQuant quant.QuantType
  39. if *quantType != "" {
  40. baseQuant = quant.QuantType(*quantType)
  41. switch baseQuant {
  42. case quant.TypeQ2K, quant.TypeQ3K, quant.TypeQ4K, quant.TypeQ5K, quant.TypeQ6K, quant.TypeQ8K:
  43. // OK
  44. default:
  45. fmt.Printf("Unknown quant type: %s\n", *quantType)
  46. os.Exit(1)
  47. }
  48. }
  49. fmt.Printf("convert: model=%s out=%s quant=%s mix=%v workers=%d max_inflight_mb=%d\n", modelPath, outputPath, baseQuant, *mixMode, *workers, *maxInFlightMB)
  50. opts := convert.Options{
  51. BaseQuant: baseQuant,
  52. MixMode: *mixMode,
  53. Workers: *workers,
  54. MaxInFlightBytes: uint64(*maxInFlightMB) * 1024 * 1024,
  55. }
  56. if err := convert.ConvertDirectory(modelPath, outputPath, opts); err != nil {
  57. fmt.Printf("convert failed: %v\n", err)
  58. os.Exit(1)
  59. }
  60. }
  61. func splitFlagsAndPositionals(argv []string) (flagArgs []string, posArgs []string) {
  62. // Known flags for this command. Values indicate whether the flag expects a separate value.
  63. expectsValue := map[string]bool{
  64. "--quant": true,
  65. "-quant": true,
  66. "--workers": true,
  67. "-workers": true,
  68. "--max-inflight-mb": true,
  69. "-max-inflight-mb": true,
  70. "--mix": false,
  71. "-mix": false,
  72. }
  73. for i := 0; i < len(argv); i++ {
  74. a := argv[i]
  75. if !strings.HasPrefix(a, "-") {
  76. posArgs = append(posArgs, a)
  77. continue
  78. }
  79. // Support --flag=value form.
  80. if strings.Contains(a, "=") {
  81. flagArgs = append(flagArgs, a)
  82. continue
  83. }
  84. flagArgs = append(flagArgs, a)
  85. if expectsValue[a] {
  86. if i+1 < len(argv) && !strings.HasPrefix(argv[i+1], "-") {
  87. flagArgs = append(flagArgs, argv[i+1])
  88. i++
  89. }
  90. }
  91. }
  92. return flagArgs, posArgs
  93. }