Files

hf2gguf

A Go 1.27 tool that converts a Hugging Face open-1b checkpoint to GGUF and verifies the result byte for byte. No Python, no torch, no pip. Library: go-gguf.

For open-1b it reproduces llama.cpp's convert_hf_to_gguf.py output exactly, down to the same SHA-256. The output is deterministic and the check is built in, so the conversion can be trusted without trusting a Python environment.

Why Go, and why this shape

hf2gguf llama.cpp convert_hf_to_gguf.py
Runtime one Go binary Python 3 with torch, numpy, transformers, gguf-py
Setup go build pip or uv, wheels, pinned versions
Startup immediate imports torch and its chain
Memory streams one tensor at a time, O(1) per tensor loads tensors through torch
Output check built-in byte-level verify none
Output byte-identical to a reference file the reference
Scope open-1b only many architectures
Quantization none, F16 only many quant types
Dependencies go-gguf a large Python tree

The advantages that matter here:

  • No Python runtime. Nothing to install or pin, and it runs where Python is unavailable or unwanted.
  • Deterministic output, checked byte for byte against a reference.
  • Small and readable. One binary, one small module, no large dependency tree.
  • Streaming. Tensors are converted one at a time; memory does not grow with the model size.
  • Easy to build and ship. go build and a single file in CI images.

Limits

  • Narrow by design. It maps one Llama-derived architecture and does not claim to convert arbitrary models.
  • Metadata is copied from a reference GGUF, not derived from the config alone. The config and tokenizer are parsed and validated, but the reference stays authoritative.
  • F16 only. There is no quantization to Q4, Q8, or other ggml types.
  • It depends on a third-party GGUF writer for serialization.

Build

go build ./...
go vet ./...
go test -race ./...        # the full test includes the 3.2 GB byte comparison

Commands

dump

Reads the reference GGUF and writes a JSON manifest of all metadata pairs (key, type, value, or array element type, count, and raw_sha256) and every tensor (name, shape, ggml type, byte count).

go run . dump -gguf models/open-1b-sft-f16.gguf -o models/open-1b-sft-f16.manifest.json

go-gguf returns an empty value for BTypeArray, so rawkv.go walks the KV section directly to keep the tokenizer arrays as raw bytes. Writing still goes through the go-gguf writer.

convert

go run . convert \
  -ref              models/open-1b-sft-f16.gguf \
  -safetensors      models/open-1b-sft/model.safetensors \
  -config           models/open-1b-sft/config.json \
  -tokenizer-config models/open-1b-sft/tokenizer_config.json \
  -tokenizer        models/open-1b-sft/tokenizer.json \
  -o                models/open-1b-sft-f16-go.gguf
  • Metadata is copied verbatim from the reference.
  • Tensors are mapped by name, with blocks.i.ffn.w_gate_up.weight split into ffn_gate and ffn_up, and *.weight_scale dropped.
  • Payloads stream from safetensors and convert F32 to F16 on the fly, using round-to-nearest, ties to even.
  • The config and tokenizer flags are optional. When given, they are parsed and validated against the reference; a mismatch is fatal.

verify

go run . verify -ref models/open-1b-sft-f16.gguf -got models/open-1b-sft-f16-go.gguf

Compares metadata on raw wire bytes and tensors by shape, type, and full payload. Reports the first differing offset on failure.

metadata: 39 checked, 0 mismatches
tensors:  220 checked, 220 byte-identical, 0 mismatches
verification PASSED

Layout

File Purpose
main.go dispatch, manifest types, dump
rawkv.go read-only GGUF KV walker with array support
safetensors.go safetensors header and byte access
hfconfig.go HF config and tokenizer parsing, validation
convert.go metadata reproduction, tensor mapping, F32 to F16
verify.go verify command and comparison engine
convert_test.go F16 rounding, KV round-trip, full-file verification