Model or dataset
Epistates/pmetal avatar
Epistates/pmetal

PMetal: a Rust ML stack for Apple Silicon, from Metal kernels to a 20-tab TUI

PMetal: high-performance Apple Silicon framework for local LLM inference, LoRA/QLoRA fine-tuning, serving, quantization, and MLX/Metal acceleration.

314 stars27 forksRustNOASSERTION

At a glance

What is it?
PMetal bundles LoRA training, GGUF quantization, model merging, distillation and an OpenAI-compatible server into one Rust CLI plus a TUI and a Tauri GUI. The scope is unusually wide for a project at v0.5.0, and the licence metadata is unresolved, so verify both before committing.
Who is it for?
Adopt PMetal if you are already committed to macOS on Apple Silicon and want fine-tuning, quantization and serving behind one Rust binary instead of a Python stack. Do not adopt it if you need Linux or CUDA, if you need a stable long-term API at v0.5.0, or if your workflow depends on the Python training ecosystem.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What PMetal is for, and who should care

PMetal is a machine learning SDK, framework and application suite for Apple Silicon, written in Rust. The README describes it as covering everything from Metal GPU kernels and Apple Neural Engine integration up to training APIs, a terminal TUI and a desktop GUI. The intended audience is narrow and specific: people who want to fine-tune and ship models on a Mac without leaving the Apple ecosystem. The README's own framing is "Ship fine-tuned models without leaving the Apple ecosystem."

The problem it addresses is fragmentation. A typical local fine-tuning workflow on a Mac pulls in a Python environment for training, a separate converter for GGUF, another tool for merging adapters, and a fourth for serving. PMetal puts all of those behind a single CLI with subcommands: train, quantize, merge, fuse, distill, grpo, eval and serve. If you already run a Mac Studio or a high-memory MacBook and you resent maintaining a Python toolchain for each step, that consolidation is the actual product. If you are on Linux or CUDA, nothing here applies to you.

The three front ends and what each one actually drives

PMetal ships three interfaces over the same core. The desktop GUI is a Tauri plus Svelte application with 19 pages, launched from crates/pmetal-gui with bun install && bun tauri dev. The README is explicit about how those pages are wired: training, inference, distillation and GRPO run in-process with real-time progress updates, while the remaining pages drive the pmetal CLI as a subprocess, and the app bundles that binary. That split matters. A GUI page like Quantize or Merge is a form that shells out, so its behaviour is bounded by whatever the CLI subcommand supports.

The terminal TUI is invoked with pmetal tui and exposes 20 tabs, including Device, Models, Datasets, Tokenize, Training, Embed Train, Pretrain, Distillation, RLKD, GRPO, Dashboard, Inference, DFlash, Serve, Quantize, Merge, Bench, Eval, Ollama and Jobs. The README lists keybindings: Ctrl+P to jump to any tab with type-to-filter, Tab and Shift+Tab to cycle, Alt+1-9 or Ctrl+1-9 for the first nine, L to adjust the learning rate mid-run, ? for contextual help, and q to quit. The Dashboard tab draws live loss curves in braille characters alongside an LR schedule and throughput sparklines, which is the kind of thing that is genuinely awkward to reproduce in a terminal without a dedicated renderer.

The CLI is the substrate. Every command in the README's table maps to a subcommand, and the TUI and GUI are layers above it. If you plan to automate anything, the CLI is the surface to learn.

Training, distillation and the MTP predictor path

The default training path is LoRA fine-tuning with sequence packing. The README's example is pmetal train --model Qwen/Qwen3-0.6B --dataset train.jsonl --output ./output --lora-r 16 --batch-size 4 --learning-rate 2e-4. That gives you a LoRA adapter at ./output/lora_weights.safetensors, which you can then either fuse into the base model with pmetal fuse --model Qwen/Qwen3-0.6B --lora ./output/lora_weights.safetensors, or load at inference time with pmetal infer --model Qwen/Qwen3-0.6B --lora ./output/lora_weights.safetensors --prompt "Explain quantum entanglement" --chat. QLoRA is listed as a supported variant of the same train command, though the README does not show a QLoRA invocation directly.

Beyond plain SFT, the command table lists train-mtp for Gemma 4 assistant or Qwen3Next/Qwen3.6 MTP predictor checkpoints, train-draft for DFlash block-diffusion draft checkpoints, and train-diffusion for LoRA/QLoRA fine-tuning of a DiffusionGemma block-diffusion model. The README shows the MTP flow as three steps: tokenize a corpus into shards with pmetal tokenize --input train.jsonl --output ./tok --tokenizer Qwen/Qwen3.6-30B-A3B-Instruct, train the predictor with pmetal train-mtp --model Qwen/Qwen3.6-30B-A3B-Instruct --family qwen3-next --shards ./tok/shard_00000.bin --output ./qwen-mtp, then use it at inference with --mtp --mtp-model ./qwen-mtp. The --family flag is doing real work there: the predictor architecture is selected by name, not inferred.

Distillation supports online, offline and progressive modes according to the README, invoked as pmetal distill --teacher Qwen/Qwen3-4B --student Qwen/Qwen3.5-0.8B-Base --dataset train.jsonl. GRPO and DAPO reasoning training is a separate subcommand with reward functions and sampling parameters, shown as pmetal grpo --model Qwen/Qwen3-0.6B --dataset reasoning.jsonl --reasoning-rewards, and the table adds that it covers VLM, speculative and async rewards. RLKD, reinforcement learning with knowledge distillation, is its own command. That is a lot of training surface for a v0.5.0 project, and the README does not document the hyperparameters for any of the newer paths beyond what appears in the examples.

Serving, quantization and merging in practice

The serve subcommand starts an OpenAI- and Anthropic-compatible inference server: pmetal serve --model Qwen/Qwen3-0.6B --port 8080. One caveat is buried in the README's own comment above that command. The server is present in the prebuilt binary and the Homebrew formula, but if you build PMetal yourself you need to add --features serve. That is a build-time feature gate, not a runtime flag, so a self-compiled binary without it will simply not have the subcommand. Anyone planning to deploy PMetal behind an existing OpenAI client should confirm which artifact they are installing before they write integration code.

Quantization goes through pmetal quantize --model ./output --output model.gguf --method q4_k_m, and the command table states 24 GGUF methods are supported. The TUI's Quantize tab adds MLX quantization with bit and method selection, so GGUF and MLX are both targets. Merging is a separate subcommand with 12 strategies; the README example uses SLERP: pmetal merge --model-a model-a --model-b model-b --method slerp --t 0.5. The TUI's Merge tab names SLERP, TIES, DARE and linear as available methods, which is a subset of the 12 the CLI table claims.

Two smaller pieces round this out. pmetal search "qwen 0.6b" --detailed queries HuggingFace Hub and includes a memory fit estimate, which is the operation you actually want before downloading a multi-gigabyte checkpoint onto a laptop. And pmetal ollama provides modelfile, create and templates subcommands for exporting to Ollama. Evaluation is pmetal eval --model Qwen/Qwen3-0.6B --dataset eval.jsonl, described as perplexity evaluation against a dataset.

Where PMetal is thin or the wrong tool

The licence situation is the first thing to resolve. The repository metadata reports NOASSERTION, while the README carries a badge reading MIT/Apache-2.0 and links to a LICENSE file. Those two signals do not agree, and the discrepancy is not explained anywhere in the supplied material. For a dependency you intend to build a product on, that is a blocker to clear with the maintainers rather than something to assume away. I am not giving legal advice here; the point is simply that the machine-readable licence field and the README badge disagree, and only one of them can be authoritative.

The second constraint is platform. This is a macOS-only stack built on Metal, MLX and the Apple Neural Engine. There is no Linux or CUDA path described. If your training runs on rented GPU instances, PMetal is not a candidate at all, and the consolidation benefit evaporates the moment part of your pipeline lives off-device.

The third is surface area against maturity. The README lists roughly two dozen CLI subcommands, 20 TUI tabs, 19 GUI pages, 24 quantization methods and 12 merge strategies at version 0.5.0, with releases at v0.3.7, v0.4.0 and v0.5.0 spaced weeks apart in early to mid 2026. That cadence is fast, and the README documents example invocations more thoroughly than it documents the parameters behind them. For the newer paths in particular (train-draft, train-diffusion, RLKD, the MTP family flag), the supplied material gives you a command line and a one-line description, not a reference. Expect to read source or experiment to tune anything. A project this broad at this version number is also a reasonable bet to have rough edges in the less-travelled subcommands.

Finally, the README does not state memory requirements, supported model architectures beyond the Qwen and Gemma examples, or what happens when a model does not fit. The search subcommand's memory fit estimation implies that fitting is a real concern, but the failure mode when it does not fit is not described.

How it differs from mlx-lm and llama.cpp

The closest comparison is Apple's own MLX and the mlx-lm package. Both target Apple Silicon, and PMetal's topics list includes mlx, so PMetal is building on that layer rather than replacing it. The difference is scope and language. mlx-lm is a Python library: you write training scripts, you import modules, you compose your own loop. PMetal is a Rust binary with subcommands, so the unit of work is a command line rather than an API call, and the training loop is not something you edit. If you want to modify the loss function or the data collator, mlx-lm gives you the seam and PMetal does not appear to.

Against llama.cpp, the split is different again. llama.cpp is a C++ inference engine with GGUF as its native format and a well-known quantization toolchain; PMetal's quantize subcommand emits GGUF and supports 24 methods, so the two overlap at that boundary. But llama.cpp is not a fine-tuning framework, and PMetal's train, distill, grpo, rlkd and embed-train subcommands have no counterpart there. Conversely, llama.cpp runs on Linux, Windows and CUDA hardware, and PMetal does not. The honest summary: choose PMetal when training and serving need to live in the same Apple-only tool, and choose llama.cpp when portability of the inference artifact matters more than where it was trained.

Maintenance, upgrades and what to check before adopting

The release history in the supplied material shows v0.3.7, v0.4.0 and v0.5.0 within roughly two months, and the repository's last push is dated 2026-09-10, later than the most recent release. That pattern suggests active development with releases lagging commits. For an adopter, the practical consequence is that a build from main and a build from the v0.5.0 tag are not the same thing, and the README's command examples may describe either.

Upgrade cost is hard to estimate from the README alone, because it does not document a stability policy or a changelog beyond release names. What it does show is a large command surface, and a large surface at a low version number is where breaking changes tend to land. If you script against pmetal train or pmetal serve, pin the version and read the release notes for each bump rather than tracking main.

The GUI adds a separate dependency chain: crates/pmetal-gui needs bun and the Tauri toolchain on top of Rust, and it bundles the pmetal binary. If you only need training and serving, the CLI avoids that entirely. Rust 1.89 or later is required per the README badge.

On licensing, the only concrete step available from this material is to open the LICENSE file in the repository and reconcile it with the NOASSERTION metadata before you depend on PMetal in anything you distribute. That is a five-minute check and it is the one thing here that can invalidate an adoption decision on its own.

Editorial conclusion

Adopt PMetal if you are already committed to macOS on Apple Silicon and want fine-tuning, quantization and serving behind one Rust binary instead of a Python stack. Do not adopt it if you need Linux or CUDA, if you need a stable long-term API at v0.5.0, or if your workflow depends on the Python training ecosystem. Before installing, resolve two things: the repository reports NOASSERTION for its licence while the README badge shows MIT/Apache-2.0, and you should confirm whether the prebuilt binary or Homebrew formula includes the serve feature, since the README states it is only present there and requires --features serve in a self-built binary.

Official sources

  1. Epistates/pmetal on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Community notes

Community notes