""" scripts/export_gguf.py — Sovereign MiMo-4B → GGUF Export Convert pruned checkpoint to GGUFv3 format for Ollama deployment. No HuggingFace dependency. Direct PyTorch → GGUF conversion. Usage: python scripts/export_gguf.py \ --checkpoint checkpoints/mimo-4b-pruned.pt \ --config config/architecture.json \ --output sovereign-mimo-4b-q4km.gguf Then create Ollama model: ollama create sovereign-mimo-4b -f Modelfile """ from __future__ import annotations import argparse import json import os import struct import sys from pathlib import Path import numpy as np import torch # ── GGUFv3 constants ───────────────────────────────────────────────────────── GGUF_MAGIC = 0x46554747 # "GGUF" GGUF_VERSION = 3 # Types GGUF_TYPE_UINT8 = 0 GGUF_TYPE_INT8 = 1 GGUF_TYPE_UINT16 = 2 GGUF_TYPE_INT16 = 3 GGUF_TYPE_UINT32 = 4 GGUF_TYPE_INT32 = 5 GGUF_TYPE_FLOAT32 = 6 GGUF_TYPE_BOOL = 7 GGUF_TYPE_STRING = 8 GGUF_TYPE_ARRAY = 9 GGUF_TYPE_UINT64 = 10 GGUF_TYPE_INT64 = 11 GGUF_TYPE_FLOAT64 = 12 # Quantization GGUF_QUANT_F32 = 0 GGUF_QUANT_F16 = 1 GGUF_QUANT_Q4_0 = 2 GGUF_QUANT_Q4_1 = 3 GGUF_QUANT_Q4_K_M = 8 GGUF_QUANT_Q8_0 = 6 GGUF_QUANT_Q8_K = 7 # ── GGUF writer ────────────────────────────────────────────────────────────── class GGUFWriter: def __init__(self, path: str): self.path = path self.f = open(path, "wb") self.keys = {} self.tensors = [] def write_header(self, n_tensors: int, n_keys: int): self.f.write(struct.pack(" bytes: """Quantize tensor to Q4_0 format.""" flat = tensor.flatten() n = len(flat) block_size = 32 n_blocks = (n + block_size - 1) // block_size result = bytearray() for i in range(n_blocks): block = flat[i * block_size : min((i + 1) * block_size, n)] # Pad block to 32 elements if len(block) < block_size: block = np.pad(block, (0, block_size - len(block))) dmax = np.abs(block).max() scale = dmax / 7.0 if dmax > 0 else 0.0 result += struct.pack(" bytes: """Quantize tensor to Q8_0 format.""" flat = tensor.flatten() block_size = 32 n_blocks = (len(flat) + block_size - 1) // block_size result = bytearray() for i in range(n_blocks): block = flat[i * block_size : min((i + 1) * block_size, len(flat))] if len(block) < block_size: block = np.pad(block, (0, block_size - len(block))) dmax = np.abs(block).max() scale = dmax / 127.0 if dmax > 0 else 0.0 result += struct.pack("