Source code for mojolearn.models.safetensors

# SPDX-License-Identifier: Apache-2.0
# Copyright 2026 Andrew Hendel. Part of mojolearn, https://doi.org/10.5281/zenodo.22068632
"""A pure-Python reader of the `.safetensors` format (lane/model-loader,
2026-09-17), returning `mojolearn.Array` tensors.

THE FORMAT. Eight bytes, a little-endian uint64 `N`; then `N` bytes of JSON,
an object whose keys are tensor names (plus an optional `__metadata__`) and
whose values are `{"dtype": ..., "shape": [...], "data_offsets": [begin,
end]}` with the offsets relative to the byte after the header; then the
tensor bytes, row-major, little-endian, back to back. A sharded checkpoint
adds `model.safetensors.index.json`, `{"weight_map": {name: file}}`, one
file per shard beside it.

WHAT MOVES AND WHAT DOES NOT. Every tensor is memory-mapped and copied ONCE
into an owned `Array` (a `memoryview` of the map, cast to the element type,
then one `Array.copy()`); a shard is never read whole into a Python `bytes`,
and never twice. The dtypes and what each becomes:

    F32    float32 as stored, bit for bit
    BF16   the uint16 bits; widened to float32 EXACTLY by the shift through
           `mojolearn.lowbit` (contract L-1, the one spelling the inference
           classes materialize with), or kept as bits when the caller asks
           for `bf16="bits"` (a 2-D tensor then comes back as a
           `lowbit.BF16Weight`, the form a block accepts directly)
    F16    widened to float32 EXACTLY by bit construction here (`_f16_bits_to_f32`:
           sign, exponent rebias by 112, mantissa shift by 13, subnormals
           renormalized, infinities and NaNs carried), no library cast
    I32    int32 as stored
    I64    int64 as stored

Every other dtype (F64, F8_*, U8, I8, I16, U16, BOOL, ...) is refused BY
NAME with the tensor's name; nothing is downcast. This reader moves no
arithmetic bit, so it takes no DEVIATION number.
"""
import json
import mmap
import os
import struct

from .. import lowbit as _lowbit
from .._array import Array
from .._buffer import frombytes

__all__ = ["SafetensorsFile", "Checkpoint", "TensorInfo", "DTYPES", "INDEX_NAME", "SINGLE_NAME"]

#: safetensors dtype name -> (Array typestr, memoryview cast code, itemsize)
DTYPES = {
    "F32": ("<f4", "f", 4),
    "F16": ("<f2", "e", 2),
    "BF16": ("<u2", "H", 2),
    "I32": ("<i4", "i", 4),
    "I64": ("<i8", "q", 8),
}

INDEX_NAME = "model.safetensors.index.json"
SINGLE_NAME = "model.safetensors"

_HEADER_LIMIT = 100 * 1024 * 1024  # a header above this is not a checkpoint


class TensorInfo:
    """One header entry: `name`, `dtype` (the safetensors spelling), `shape`
    (a tuple), `begin`/`end` (offsets into the data section) and `file`."""

    __slots__ = ("name", "dtype", "shape", "begin", "end", "file")

    def __init__(self, name, dtype, shape, begin, end, file):
        self.name = name
        self.dtype = dtype
        self.shape = shape
        self.begin = begin
        self.end = end
        self.file = file

    @property
    def nbytes(self):
        return self.end - self.begin

    @property
    def size(self):
        n = 1
        for s in self.shape:  # glue: product of the tensor shape entries
            n *= s
        return n

    def __repr__(self):
        return f"TensorInfo({self.name!r}, {self.dtype}, shape={self.shape})"


def widen_f16(bits):
    """A float16 Array of any rank (`'<f2'`, or its bits as `'<u2'`) to
    float32, by bit construction: a normal value keeps its mantissa shifted
    up by 13 and rebiases the exponent by 112, a subnormal is renormalized
    into a normal float32, zero keeps its sign, and the infinities and
    every NaN keep sign and payload. The construction is
    `gemm/contract.mojo::f16_bits_to_f32`, run by the linalg binding's
    `from_f16` (a device kernel on a GPU install, the linalg host binding
    on a CPU-only one); the per-element Python loop is gone
    (pyglue-text-io, Oct 3). Exact, so every column has the same bits."""
    from .. import _linalg_impl
    shape = tuple(bits.shape)
    if bits.dtype != "<u2":
        bits = frombytes(bits.tobytes(), "<u2", shape if shape else (1,))
    if bits.size == 0:
        return frombytes(b"", "<f4", shape)
    out = _linalg_impl.from_f16(bits)
    return out.reshape(shape) if shape != tuple(out.shape) else out


def _parse_header(raw, path):
    try:
        header = json.loads(raw.decode("utf-8"))
    except (ValueError, UnicodeDecodeError) as exc:
        raise ValueError(f"mojolearn.models.safetensors: {path}: the header is not JSON: {exc}") from None
    if not isinstance(header, dict):
        raise ValueError(f"mojolearn.models.safetensors: {path}: the header is not a JSON object")
    return header


[docs] class SafetensorsFile: """One `.safetensors` file. `names()`, `info(name)`, `read(name)`; a context manager that closes the map. The map is opened on the first read and released by `close()`.""" def __init__(self, path): self.path = os.fspath(path) self._fh = None self._mm = None with open(self.path, "rb") as fh: head = fh.read(8) if len(head) != 8: raise ValueError(f"mojolearn.models.safetensors: {self.path} is shorter than its 8-byte header length") (n,) = struct.unpack("<Q", head) if n > _HEADER_LIMIT: raise ValueError(f"mojolearn.models.safetensors: {self.path}: header length {n} exceeds {_HEADER_LIMIT}") raw = fh.read(n) if len(raw) != n: raise ValueError(f"mojolearn.models.safetensors: {self.path}: header is truncated ({len(raw)} of {n} bytes)") fh.seek(0, os.SEEK_END) total = fh.tell() self.data_start = 8 + n self.data_size = total - self.data_start header = _parse_header(raw, self.path) self.metadata = header.pop("__metadata__", None) self._infos = {} for name, entry in header.items(): # cpu-route: parses the safetensors file header (file input) if not isinstance(entry, dict) or not {"dtype", "shape", "data_offsets"} <= set(entry): raise ValueError(f"mojolearn.models.safetensors: {self.path}: tensor {name!r} lacks dtype/shape/data_offsets") dtype = entry["dtype"] shape = tuple(int(s) for s in entry["shape"]) # cpu-route: parses the safetensors file header (file input) begin, end = (int(v) for v in entry["data_offsets"]) # cpu-route: parses the safetensors file header (file input) if begin < 0 or end < begin or end > self.data_size: raise ValueError(f"mojolearn.models.safetensors: {self.path}: tensor {name!r} offsets [{begin}, {end}) fall outside the {self.data_size}-byte data section") if dtype in DTYPES: size = 1 for s in shape: # cpu-route: parses the safetensors file header (file input) size *= s if size * DTYPES[dtype][2] != end - begin: raise ValueError(f"mojolearn.models.safetensors: {self.path}: tensor {name!r} of {dtype} shape {shape} needs {size * DTYPES[dtype][2]} bytes, the offsets give {end - begin}") self._infos[name] = TensorInfo(name, dtype, shape, begin, end, self.path) # ------------------------------------------------------------- listing def names(self): return list(self._infos) def info(self, name): try: return self._infos[name] except KeyError: raise KeyError(f"mojolearn.models.safetensors: {self.path} holds no tensor {name!r}") from None def __contains__(self, name): return name in self._infos # ------------------------------------------------------------- reading def _map(self): if self._mm is None: self._fh = open(self.path, "rb") self._mm = mmap.mmap(self._fh.fileno(), 0, access=mmap.ACCESS_READ) return self._mm
[docs] def read(self, name, *, bf16="widen"): """The tensor `name` as an owned `Array` (see the module header for what each dtype becomes). `bf16` is "widen" (float32) or "bits" (the uint16 bits; a 2-D tensor as a `lowbit.BF16Weight`).""" if bf16 not in ("widen", "bits"): raise ValueError(f"mojolearn.models.safetensors: bf16 must be 'widen' or 'bits', got {bf16!r}") t = self.info(name) if t.dtype not in DTYPES: raise TypeError( f"mojolearn.models.safetensors: tensor {name!r} in {self.path} has dtype {t.dtype}, " f"which this reader refuses by name; it reads {sorted(DTYPES)} and downcasts nothing") typestr, code, itemsize = DTYPES[t.dtype] mm = self._map() view = memoryview(mm) try: raw = view[self.data_start + t.begin:self.data_start + t.end] try: if t.size == 0: owned = frombytes(b"", typestr, t.shape) elif code == "e": # memoryview.cast("e") exists only from Python 3.12, and the # wheel supports 3.10 up; float16 is widened from its bits # below, so its bytes are copied as they are. owned = frombytes(raw.tobytes(), "<u2", t.shape if t.shape else (1,)) if not t.shape: owned = owned.reshape(()) else: shape = t.shape if t.shape else (1,) typed = raw.cast("B").cast(code, shape) try: owned = Array.from_buffer(typed).copy() # the one copy finally: typed.release() if not t.shape: owned = owned.reshape(()) finally: raw.release() finally: view.release() if t.dtype == "F16": return widen_f16(owned) if t.dtype == "BF16": if bf16 == "bits": return _lowbit.BF16Weight(owned) if owned.ndim == 2 else owned return _lowbit.widen_bf16(owned) return owned
def close(self): if self._mm is not None: self._mm.close() self._mm = None if self._fh is not None: self._fh.close() self._fh = None def __enter__(self): return self def __exit__(self, *exc): self.close() def __del__(self): try: self.close() except Exception: # noqa: BLE001 pass def __repr__(self): return f"SafetensorsFile({self.path!r}, tensors={len(self._infos)})"
[docs] class Checkpoint: """A single `model.safetensors`, an explicit `.safetensors` path, or a directory holding `model.safetensors.index.json` and its shards. Shards are opened on first use and closed by `close()`.""" def __init__(self, files, weight_map, root): self.root = root self._paths = list(files) self._weight_map = dict(weight_map) self._open = {} self._infos = None @classmethod def open(cls, path): path = os.fspath(path) if os.path.isdir(path): index = os.path.join(path, INDEX_NAME) single = os.path.join(path, SINGLE_NAME) if os.path.isfile(index): with open(index, "r", encoding="utf-8") as fh: data = json.load(fh) wm = data.get("weight_map") if isinstance(data, dict) else None if not isinstance(wm, dict) or not wm: raise ValueError(f"mojolearn.models.safetensors: {index} has no weight_map") files = sorted(set(os.path.join(path, f) for f in wm.values())) # cpu-route: lists the checkpoint shard files (file input) missing = [f for f in files if not os.path.isfile(f)] # cpu-route: checks the checkpoint shard files exist (file input) if missing: raise FileNotFoundError(f"mojolearn.models.safetensors: {index} names shards that do not exist: {missing}") return cls(files, {n: os.path.join(path, f) for n, f in wm.items()}, path) # cpu-route: maps weight names to shard files (file input) if os.path.isfile(single): return cls([single], {}, path) others = sorted(f for f in os.listdir(path) if f.endswith(".safetensors")) # cpu-route: lists the checkpoint shard files (file input) if len(others) == 1: one = os.path.join(path, others[0]) return cls([one], {}, path) raise FileNotFoundError( f"mojolearn.models.safetensors: {path} holds neither {SINGLE_NAME} nor {INDEX_NAME}" + (f" (found {others}; one unnamed shard set is not an index)" if others else "")) if os.path.isfile(path): return cls([path], {}, os.path.dirname(path) or ".") raise FileNotFoundError(f"mojolearn.models.safetensors: {path} does not exist") def _file(self, path): f = self._open.get(path) if f is None: f = SafetensorsFile(path) self._open[path] = f return f def _index(self): if self._infos is None: infos = {} for p in self._paths: # cpu-route: indexes the checkpoint shard files (file input) f = self._file(p) for n in f.names(): # cpu-route: indexes the checkpoint tensor names (file input) if n in infos: raise ValueError(f"mojolearn.models.safetensors: tensor {n!r} appears in both {infos[n].file} and {p}") infos[n] = f.info(n) for n, p in self._weight_map.items(): # cpu-route: indexes the checkpoint weight map (file input) if n not in infos: raise ValueError(f"mojolearn.models.safetensors: the index maps {n!r} to {p} but that shard does not hold it") self._infos = infos return self._infos def names(self): return list(self._index()) def info(self, name): try: return self._index()[name] except KeyError: raise KeyError(f"mojolearn.models.safetensors: no tensor {name!r} under {self.root}") from None def __contains__(self, name): return name in self._index() def read(self, name, *, bf16="widen"): t = self.info(name) return self._file(t.file).read(name, bf16=bf16) def close(self): for f in self._open.values(): # cpu-route: closes the open checkpoint files (file input) f.close() self._open = {} def __enter__(self): return self def __exit__(self, *exc): self.close() def __repr__(self): return f"Checkpoint({self.root!r}, shards={len(self._paths)})"
def write_safetensors(path, tensors, metadata=None): """WRITE a `.safetensors` file from `{name: (dtype, shape, bytes)}`, the tests' synthetic-checkpoint writer. `dtype` is the safetensors spelling, `bytes` the row-major little-endian payload. Kept beside the reader so the two agree on the one format; not a checkpoint saver for models.""" header = {} if metadata is not None: header["__metadata__"] = dict(metadata) offset = 0 payload = [] for name in sorted(tensors): # cpu-route: writes the safetensors header in name order (file output) dtype, shape, raw = tensors[name] header[name] = {"dtype": dtype, "shape": list(shape), "data_offsets": [offset, offset + len(raw)]} payload.append(raw) offset += len(raw) encoded = json.dumps(header, separators=(",", ":")).encode("utf-8") pad = (8 - len(encoded) % 8) % 8 encoded += b" " * pad with open(path, "wb") as fh: fh.write(struct.pack("<Q", len(encoded))) fh.write(encoded) for raw in payload: # cpu-route: writes the tensor payloads (file output) fh.write(raw) return path