Skip to main content

reminis

Your model's weights are just data. Store them in a database.

reminis converts any GGUF or safetensors model into a SQLite database where every tensor becomes a queryable, versionable, diffable row. Convert back when you're done. Lossless. Fast.

pip install reminis

Why

The ML world treats model weights as opaque files. You save the whole thing, load the whole thing, and if something goes wrong, you retrain from scratch.

Once weights are in a database, you get — for free — everything that 40 years of database engineering has built: queries, rollback, diffs, branching, merging, audit logs, access control, replication.

Quick Start

# Convert a GGUF model to SQLite
reminis convert model.gguf

# ...or a safetensors model. Point at the file, the index, or the directory;
# sharded checkpoints and config.json are handled for you.
reminis convert ./Llama-3.2-1B/

# Turn a peft LoRA adapter into a delta pack against its base
reminis lora ./my-adapter/ base.db -o capability.pack.db

# Inspect what's inside
reminis info model.db

# Browse it in your browser
reminis view model.db

# Compare two models, and package the difference
reminis diff base.db finetuned.db -o change.delta.db

# Reconstruct the fine-tune from the base plus the pack
reminis apply base.db change.delta.db -o rebuilt.db

# Convert back to GGUF
reminis export model.db -o model_restored.gguf

Verified Results

Architectures

Every tensor is SHA256-hashed before and after the round-trip. These architectures are confirmed lossless:

Architecture Model Tensors Size Convert Export Result
llama Mistral-7B-Instruct-v0.3 Q4_K_M 291 4170 MB 18.5s 4.4s lossless
llama Llama-3.2-1B-Instruct F16 147 2365 MB 19.2s 1.7s lossless
qwen2 Qwen2.5-0.5B-Instruct FP16 291 1208 MB 14.4s 0.9s lossless
granitemoe Granite-3.1-1B-A400M Q4_K_M 242 784 MB 6.5s 0.5s lossless
clip (vision) SmolVLM-256M projector F16 198 181 MB 1.0s 0.1s lossless
llama SmolLM-135M, 9 quantizations 272 84–258 MB ~2s ~0.1s lossless

The MoE model is the interesting one: 72 of its tensors are 3-dimensional expert stacks (e.g. [512, 1024, 32] in Q6_K), a shape the quantized export path had never seen. It generalizes correctly.

Throughput is roughly linear with size — about 120 MB/s converting, over 1 GB/s exporting.

Safetensors

Same SHA256 verification, on real downloaded checkpoints:

Model Dtype Tensors Size Convert Export Result
SmolLM-135M F32 272 513 MB 1.9s 0.2s lossless
SmolLM2-135M-Instruct BF16 272 257 MB 1.0s 0.1s lossless

BF16 is the case that matters: it is what most fine-tuning emits, and it is the reason reminis parses the format directly rather than through safetensors.numpy, which cannot load BF16 at all. The conversion is verified bit-identical to PyTorch in both directions, across normal values, subnormals, infinities, and NaN.

LoRA adapters against peft

A rank-16 adapter on SmolLM2-135M (BF16 base, all 7 projection types targeted, 210 modules), converted to a pack, applied, and compared tensor-by-tensor against peft's own merge_and_unload():

Check Result
Tensors byte-identical to peft's merge 272 / 272
Worst relative difference 0.000e+00
Worst gap in BF16 representable steps 0
Pack size 17.3 MB (6.7% of the 256.6 MB base)

Bit-exact agreement with peft, on a BF16 base where rounding could have shown up and did not.

Diff and apply at scale

Perturbing all 64 attention projections, then reconstructing from the pack:

Model Copy DB Diff Pack Apply + verify Result
Llama-3.2-1B, 2.4 GB, F16 0.57s 9.4s 278 MB (11.8%) 7.9s exact
Mistral-7B, 4.2 GB, Q4_K_M 1.78s 14.5s 15 MB (0.4%) 12.2s exact

The 7B row is quantized, so per-value deltas are not computable — but changes are still detected and encoded byte-exactly through the XOR path, which is why quantized models work at all.

Roughly half of each timing is SHA256 hashing the full model, which is what guarantees a pack cannot be applied to the wrong base. SHA256 is hardware-accelerated here (957 MB/s) and measurably faster than blake2b, so that is already the cheapest safe option.

Quantization coverage

SHA256-verified lossless round-trip across 9 SmolLM-135M variants covering 13 quantization types:

Model                               Dtypes                Tensors  GGUF MB    DB MB    RT MB  Conv(s)   Exp(s)   Result
---------------------------------------------------------------------------------------------------------------------------------------
SmolLM-135M.IQ3_M                   F32,IQ3_S,IQ4_NL,Q4_K       272     86.0     86.1     84.3     1.73     0.05     PASS
SmolLM-135M.IQ4_XS                  F32,IQ4_NL,IQ4_XS,Q5_K      272     87.1     87.1     85.4     1.81     0.06     PASS
SmolLM-135M.Q2_K                    F32,IQ4_NL,Q3_K,Q8_0         272     84.1     84.2     82.4     1.64     0.05     PASS
SmolLM-135M.Q3_K_M                  F32,IQ4_NL,Q4_K,Q5_0         272     89.2     89.3     87.5     1.67     0.06     PASS
SmolLM-135M.Q4_K_M                  F32,Q4_K,Q5_0,Q6_K,Q8_0      272    100.6    100.7     98.9     1.71     0.05     PASS
SmolLM-135M.Q5_K_M                  F32,Q5_1,Q5_K,Q6_K,Q8_0      272    106.9    106.9    105.2     1.83     0.07     PASS
SmolLM-135M.Q6_K                    F32,Q6_K,Q8_0                 272    132.0    132.0    130.3     1.84     0.07     PASS
SmolLM-135M.Q8_0                    F32,Q8_0                      272    138.1    138.2    136.4     1.92     0.08     PASS
SmolLM-135M.f16                     F16,F32                       272    258.3    258.4    256.7     2.39     0.14     PASS
---------------------------------------------------------------------------------------------------------------------------------------
9/9 models passed SHA256-verified lossless round-trip
ALL TESTS PASSED - every tensor in every model matches byte-for-byte

Every tensor in every model was hashed with SHA256 before and after the round-trip. Zero data loss.

What's in the Database

$ reminis info model.db

Database: model.db (258.4 MB)
  general.name: SmolLM 135M
  general.architecture: llama
  Metadata fields: 40
  Tensors: 272
  Parameters: 134,515,008
  Weight data: 256.6 MB

  Dtype breakdown:
    F16          211 tensors     256.5 MB
    F32           61 tensors       0.1 MB

Every tensor gets its own row with full metadata:

Column Description
name Tensor path (e.g. blk.5.attn_q.weight, or model.layers.5.self_attn.q_proj.weight from safetensors)
shape Dimensions as JSON (e.g. [576, 576])
dtype Data type (F16, F32, BF16, Q4_K, Q8_0, etc.)
dtype_id Numeric type id — see the note below
n_elements Number of parameters
n_bytes Storage size in bytes
data Raw weight data as BLOB

shape is stored reversed relative to the data layout, which is GGUF's convention; reminis keeps it for every format so one set of code reads both. A safetensors tensor of logical shape [out, in] is stored as [in, out] and un-reversed on export.

dtype_id means different things in different databases — a GGML enum value for GGUF-sourced models, a reminis-local id for safetensors ones — so model_meta records which system applies under reminis.dtype_system. Read the dtype name unless you specifically need the id.

All model metadata (architecture, context length, vocab size, etc.) is stored in a model_meta table. For safetensors models this is populated from the sibling config.json under config.* keys, since the format itself carries almost no metadata of its own.

Query Your Model

Once in SQLite, you can query weights like any database:

-- Largest tensors by parameter count
SELECT name, n_elements, n_bytes / 1024 / 1024 as mb
FROM tensors ORDER BY n_elements DESC LIMIT 5;

-- All attention weights in layer 5
SELECT name, shape, dtype FROM tensors
WHERE name LIKE 'blk.5.attn%';

-- Total size by dtype
SELECT dtype, COUNT(*) as count, SUM(n_bytes) / 1024 / 1024 as total_mb
FROM tensors GROUP BY dtype;

-- Model architecture
SELECT key, value FROM model_meta
WHERE key LIKE '%context_length%' OR key LIKE '%block_count%';

Diffing and Delta Packs

reminis diff compares two models tensor by tensor and can emit a delta pack — a small database that reconstructs the target from the base:

reminis diff base.db instruct.db -o change.delta.db
reminis apply base.db change.delta.db -o rebuilt.db

Packs record the weight hashes of both sides. apply refuses a base that does not match, rather than silently producing a corrupt model, and verifies the result against the recorded target hash.

Encoding is XOR, not arithmetic subtraction. An arithmetic float delta is not exactly reversible — b - a generally is not representable in the tensor's own dtype, so a + delta lands a rounding step away from b. XOR is exact for every dtype, and also works on quantized tensors, whose bytes cannot be subtracted meaningfully at all. Per tensor, whichever of the compressed XOR delta or a compressed full replacement is smaller wins.

How small are packs, really?

It depends entirely on how much of the model the fine-tune touched.

Scenario Tensors changed Pack size
Targeted change (5 of 272 tensors) 5 1.4% of full model
Full fine-tune (SmolLM-135M to its Instruct variant) 272 of 272 58.8% of full model

The second row is the honest one for full fine-tuning. Every tensor changed, with ~97% of individual values differing in each, so lossless compression has little to exploit — the differing float16 mantissa bits are close to incompressible.

The delta is also only partly low-rank. Ranks needed to capture 90% of the delta's energy:

Tensor Shape Rank for 90% Full rank
blk.0.attn_q.weight [576, 576] 31 576
blk.18.ffn_gate.weight [1536, 576] 311 576
blk.29.ffn_down.weight [576, 1536] 291 576

Attention deltas compress well under a low-rank factorization; FFN deltas do not. That asymmetry is exactly what --lossy exploits.

Low-rank packs for LoRA fine-tunes

A LoRA update is W + BA with BA rank-r by construction, so the delta is genuinely low-rank and needs only r*(m+n) numbers instead of m*n. Opt in with --lossy:

reminis diff base.db lora-merged.db -o change.delta.db --lossy 0.01

The tolerance is the maximum relative error allowed per tensor (default 0.01 = 1%). Measured on a rank-16 LoRA merge:

Model Lossless pack Low-rank pack Shrink Worst error
SmolLM-135M, 258 MB 38.3 MB (14.9%) 3.5 MB (1.4%) 11.0x 1.2e-04
Llama-3.2-1B, 2.4 GB 242.7 MB (10.3%) 6.4 MB (0.3%) 37.9x 1.1e-04

Achieved error lands ~100x inside the 1% budget, because rank is chosen by error target rather than fixed.

It decides per tensor, and never makes a pack worse. Low-rank is only used where it both beats the lossless size and stays inside tolerance; otherwise the lossless encoding is kept. On the full fine-tune above, only 1 of 272 tensors qualified and the pack stayed lossless — so --lossy degrades gracefully to "no change" rather than silently hurting quality.

Cost is a slower diff (SVD): roughly 4x on the 1B model. Quantized and non-2D tensors are never low-rank encoded, since their bytes cannot be decomposed.

Lossy packs are still exactly verifiable. The reconstruction is deterministic, so the pack records the hash of what apply will actually produce, and apply checks against it byte-for-byte. What it cannot promise is that this equals the original target — that divergence is carried as a recorded error bound and printed on apply:

Result verified against reconstruction hash
NOTE: this is a lossy pack (120 tensors low-rank encoded). The result is not
byte-identical to the original target; worst per-tensor relative error is 1.16e-04.

LoRA adapters ship as exact delta packs

A LoRA adapter already is a low-rank delta — peft saves lora_A and lora_B, and the update it applies is (alpha / r) * B @ A. That is the same structure as a reminis low-rank pack, except the factors are the real ones rather than an SVD approximation of a finished merge.

So reminis lora converts an adapter with no SVD, no merge, and no approximation error:

reminis lora ./my-adapter/ base.db -o capability.pack.db
reminis apply base.db capability.pack.db -o merged.db

Verified against peft itself: the applied result is compared tensor-by-tensor against merge_and_unload(), on a toy float32 model and on a real BF16 SmolLM2-135M with 210 targeted modules. Every tensor came out byte-identical to peft's own merge in both cases — not merely close. The tests still assert a tolerance rather than byte-equality, since that is the property that actually matters and a mis-applied alpha / r would blow past it by orders of magnitude.

modules_to_save tensors — ones peft trained outright rather than through a factor pair — are carried in the pack in full. Embedding LoRA (lora_embedding_A/B) is not handled yet, and reminis refuses such an adapter rather than writing a pack that quietly omits part of it.

Python API

from reminis import gguf_to_sqlite, safetensors_to_sqlite, sqlite_to_gguf

# Convert, from either format
db_path = gguf_to_sqlite("model.gguf")
db_path = safetensors_to_sqlite("./Llama-3.2-1B/")

# Query with standard sqlite3
import sqlite3
conn = sqlite3.connect(db_path)
for name, n_elements in conn.execute(
    "SELECT name, n_elements FROM tensors ORDER BY n_elements DESC LIMIT 5"
):
    print(f"{name}: {n_elements:,} params")

# Export back
sqlite_to_gguf(db_path, "model_restored.gguf")

Supported Formats

GGUF

All GGUF tensor types are supported and verified, including:

Type Description Verified
F32, F16 Full precision Yes
Q4_K, Q5_K, Q6_K K-quants (4/5/6 bit) Yes
Q8_0 8-bit quantized Yes
Q3_K, Q5_0, Q5_1 Other quants Yes
IQ3_S, IQ4_NL, IQ4_XS Importance-weighted quants Yes

Safetensors

Read and written with numpy alone — the format is an 8-byte header length, a JSON header, and raw bytes, so no extra dependency is needed. Single-file, sharded (model.safetensors.index.json), and directory inputs all work, and a sibling config.json is ingested into model_meta and rebuilt on export.

BF16 is fully supported, which is the point: it is what most fine-tuning produces, and safetensors.numpy cannot load it at all. reminis converts BF16 with round-to-nearest-even, verified bit-identical to PyTorch in both directions.

Note that GGUF and safetensors use different tensor names (blk.0.attn_q.weight vs model.layers.0.self_attn.q_proj.weight) and different dtype_id spaces, so:

  • Exporting a safetensors-sourced database as GGUF is refused, rather than writing a file whose dtype ids mean something else.
  • Exporting a GGUF-sourced database as safetensors works when every dtype has an equivalent (F32/F16/BF16 and the integer types). Quantized GGML types have none, and are refused.
  • Diffing a GGUF base against a safetensors fine-tune is not supported; the fingerprint check catches it and fails clearly.

Roadmap

  • Publish to PyPI
  • GGUF to SQLite converter (lossless, verified across 13 quant types)
  • SQLite to GGUF back-converter (lossless, byte-perfect)
  • SHA256 verification test suite
  • Interactive HTML viewer (reminis view)
  • Weight diffing between model versions (reminis diff)
  • Delta packs with verified apply (reminis apply)
  • Validated to 7B across llama / qwen2 / granitemoe / clip
  • Low-rank delta encoding for LoRA fine-tunes (--lossy)
  • Safetensors input and output, sharded and BF16 (reminis convert ./model/)
  • peft LoRA adapters as exact delta packs (reminis lora)
  • Fine-tune tracking with edit logs
  • Surgical rollback of bad training steps
  • Model merging via SQL operations
  • Inference from database-stored weights
  • Unsloth integration

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

reminis-0.4.0.tar.gz (58.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

reminis-0.4.0-py3-none-any.whl (50.8 kB view details)

Uploaded Python 3

File details

Details for the file reminis-0.4.0.tar.gz.

File metadata

  • Download URL: reminis-0.4.0.tar.gz
  • Upload date:
  • Size: 58.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.31 {"installer":{"name":"uv","version":"0.11.31","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for reminis-0.4.0.tar.gz
Algorithm Hash digest
SHA256 b850fd41798ed76669078f237d53de50a31e1657c2e6046f87034994bb9691e9
MD5 47c69cc8e6110e071146a6cf6e32f8db
BLAKE2b-256 c16836d9bde609ccb49205de0743dbe8d6dd33d32b05ae1652fae299edf6ec91

See more details on using hashes here.

File details

Details for the file reminis-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: reminis-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 50.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.31 {"installer":{"name":"uv","version":"0.11.31","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for reminis-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 eb78186a9455e0f58ce1a40991ac2ac05dc4ce0632afc2a005b7695f8ef22475
MD5 4e44bde61e072831c287603b8f03a070
BLAKE2b-256 eee29ef45bae3e8e7f135820689fc39092acee6eb9ce3b963f95c56b04a3f558

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page