Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 11 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,30 @@ repository = "https://github.com/physics515/Mummu"
# `Wgpu` transparently becomes `Fusion<Wgpu>`.
burn = { version = "0.21", default-features = false, features = [
"std",
"ndarray",
"wgpu",
"fusion",
"autotune",
"store",
] }
# The CPU backend: pure-Rust SIMD + gemm, burn-ndarray's designated successor
# (0.21 release notes); swapped in 2026-07-10 after the MiniLM Candle-parity
# gate passed on it bit-for-bit-equivalent (cosine 0.99999994, max component
# diff 1.3e-7) with all 80 unit tests green.
burn-flex = "0.21"

# Direct handle on wgpu (the same version Burn resolves) for the cheap startup
# adapter probe: enumerate GPUs to decide GPU vs CPU before building a backend.
wgpu = "29"
# Block on wgpu 29's async adapter enumeration from the sync startup probe.
pollster = "0.4"
pollster = "1.0"
# f16 element type for the GPU backend; the same `half` version Burn pulls.
half = "2"
tokenizers = "0.22"

# Blocking HTTP (rustls) for the P3 model downloader; small, sync like the rest
# of the library surface.
ureq = "3"

serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2"
Expand Down
30 changes: 24 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

**A from-scratch, single-binary local model runner in Rust — import any model, quantize it to fit, and run it on all the hardware you have: CPU, one GPU, or several. No Ollama, no cloud, no CUDA toolchain.**

Mummu imports models from HuggingFace (or disk), **(auto-)quantizes** them to fit, and runs them **natively in Rust on [Burn](https://burn.dev)** across every device you have. One binary — a runtime probe inventories your GPUs (Vulkan / DX12 / Metal via `wgpu`) and your CPU (`ndarray`) and places the model to use them **to the fullest**: several GPUs together, with CPU offload when VRAM is short, no feature-split builds and no CUDA toolchain. Models are **reimplemented from scratch**, generic over the Burn backend, and **parity-tested byte-for-byte** against a reference so the reimplementations can be trusted.
Mummu imports models from HuggingFace (or disk), **(auto-)quantizes** them to fit, and runs them **natively in Rust on [Burn](https://burn.dev)** across every device you have. One binary — a runtime probe inventories your GPUs (Vulkan / DX12 / Metal via `wgpu`) and your CPU (`burn-flex`) and places the model to use them **to the fullest**: several GPUs together, with CPU offload when VRAM is short, no feature-split builds and no CUDA toolchain. Models are **reimplemented from scratch**, generic over the Burn backend, and **parity-tested byte-for-byte** against a reference so the reimplementations can be trusted.

It exists because two local-first apps — **[laurelane](https://github.com/physics515/laurelane)** (a private budgeting cockpit) and **[Nanna](https://github.com/physics515/Nanna)** (an always-on local AI presence) — were building the *same* runner twice. Mummu is that runner, extracted and generalized: each app consumes it as a dependency and keeps only its own domain glue. Laurelane proved the blueprint (Qwen2.5 / LFM2.5 / all-MiniLM ported to Burn, byte-identical parity vs Candle, validated on an RTX 4070 Ti SUPER 16 GB); Mummu is where it lives, hardens, and grows.

## What it is

- **One binary, every device** — compile both `Wgpu` (Vulkan/DX12/Metal, no CUDA toolchain) and `NdArray` (CPU); a runtime probe enumerates **all** adapters + the CPU and places the model across them — a single GPU, **several GPUs together**, or GPU + CPU hybrid. No feature-split builds, no per-vendor path.
- **One binary, every device** — compile both `Wgpu` (Vulkan/DX12/Metal, no CUDA toolchain) and `burn-flex` (CPU); a runtime probe enumerates **all** adapters + the CPU and places the model across them — a single GPU, **several GPUs together**, or GPU + CPU hybrid. No feature-split builds, no per-vendor path.
- **Models from scratch, generic over `B: Backend`** — a growing zoo (Qwen2/2.5, LFM2/2.5 hybrid conv+attention, all-MiniLM embedder) built on shared blocks (RmsNorm · GQA · RoPE · SwiGLU · tied lm-head · depthwise causal conv), with a clean trait to add more.
- **Trustworthy reimplementations** — every port must pass a **parity gate**: single-forward top-k logits *and* a short greedy sequence match a reference (Candle, or a local Ollama of the same model) exactly.
- **Fast** — per-layer KV cache (+ conv-state cache for hybrids), on-GPU argmax (sync only the winning index), sampling, **token streaming**, cooperative cancellation; kernel `fusion` + `autotune`; an opt-in **f16** path to roughly halve VRAM.
Expand All @@ -19,7 +19,7 @@ It exists because two local-first apps — **[laurelane](https://github.com/phys
## Status — what runs today

- **Workspace + backends** — `crates/mummu` (library) + `crates/mummu-bench` (criterion); one binary
compiles both `Wgpu` (with `fusion` + `autotune`) and `NdArray`, with a cached runtime GPU probe and a
compiles both `Wgpu` (with `fusion` + `autotune`) and `burn-flex` (CPU), with a cached runtime GPU probe and a
device inventory that records per-adapter/per-API `SHADER_F16`.
- **Shared blocks, generic over `B: Backend`** — cache-aware GQA attention (optional per-head q/k
RMSNorm), manual RoPE, SwiGLU, and LFM2's double-gated causal short-conv with rolling decode state;
Expand All @@ -28,9 +28,27 @@ It exists because two local-first apps — **[laurelane](https://github.com/phys
fail-loud load (never silently zero-init); `config.json`-driven hyperparameters.
- **Three models ported and running on real weights** — Qwen2/2.5, the LFM2/2.5 hybrid, and the
all-MiniLM sentence embedder; Qwen2.5-1.5B and LFM2.5-1.2B load and greedy-decode correctly on the
reference GPU (wgpu/Vulkan). The P7 parity harness will gate them "trusted" against a reference before
the roadmap ticks them done.
- **Model-cache disk accounting** — per-model disk usage + traversal-safe removal validation (`manage`).
reference GPU (wgpu/Vulkan).
- **Qwen2.5 and MiniLM are parity-verified** — the two-leg P7 gate passes for Qwen2.5-1.5B on the
reference GPU: single-forward top-5 logits match a Candle f32 reference (max |Δlogit| 2.7e-5,
`tests/parity_qwen2.rs` + the committed `tools/candle-probe` fixture) and a 24-token greedy sequence
matches `ollama qwen2.5:1.5b-instruct-fp16` byte-for-byte. The MiniLM embedder matches its Candle
reference at cosine 0.99999994 (max |Δcomponent| 1.2e-7, `tests/real_minilm.rs`). LFM2.5 still awaits
a same-weights reference (tracked in P7).
- **Sampling, streaming, cancellation** — temperature / top-k / top-p sampling (deterministic per seed),
per-token streaming through a `ControlFlow` callback, and cooperative between-token cancellation;
greedy decoding keeps the argmax on-device.
- **Benchmarked** — Qwen2.5-1.5B f32 on the reference GPU: **TTFT 100.5 ms, decode 13.3 tok/s, 11.9 GiB
whole-card peak** (~7.9 GiB runner) — recorded with budgets in [bench/BASELINE.md](bench/BASELINE.md),
enforced by an opt-in regression gate (`mummu-bench/tests/budget.rs`).
- **Model management** — `ModelManager` gives settings UIs the whole lifecycle over a declarative model
catalog (`registry::ModelSpec`): install with per-chunk download progress, `is_installed`, per-model
disk usage, and traversal-safe removal; model switching rides `ModelSlot`.
- **Hub downloads** — streaming HuggingFace fetches into the model cache: resumable (`.part` + HTTP
Range, proven byte-identical after an interrupted transfer), length-verified, shard-index aware, with
a per-chunk progress callback; verified end-to-end by downloading all-MiniLM and embedding with it.
- **Process-lifetime model cache** — `ModelSlot` loads a checkpoint once per process, switches models by
key, and `clear()`s to free VRAM; Burn's `Param` isn't `Sync`, so access serializes behind its mutex.

## Design principles

Expand Down
Loading