LFM2.5-8B-A1B β Core AI (.aimodel)
LiquidAI/LFM2.5-8B-A1B as Core AI .aimodel bundles for Apple silicon, published by
visible-cx. They load through Core AI on macOS and are
not usable by PyTorch, GGUF or MLX. This is the model the Visible app
routes Perspectives and Reports to β the long-prose families, where a 16,384-token
window and grammar-constrained decoding matter more than raw speed.
It is a sparse mixture-of-experts model: 8.3B total parameters, ~1.5B active per token.
24 layers β 18 short-convolution mixers and 6 GQA attention layers; the first two layers are
dense, every layer after is a 32-expert top-4 MoE. The expert weights are read through a
gather_qmm Metal kernel, which is what makes the model fast: upstream measured
140.4 tok/s on an M4 Max through the gather path against 39.2 tok/s for the same
weights read as a dense over-read.
aff4/is the bundle to use. It supersedesmf64/(thesym8build) on one arithmetic fact and not on preference: on a 16 GB Macsym8requires 12.72 GiB free before it will start, which is more than that machine has ever been measured to have; theaff4D2 rebuild requires 7.55β7.87 GiB and has completed an 11-question report leg end to end at 56.2 tok/s.mf64/is kept as the evidence for that comparison, not as a fallback. See Measurements.
Contents
| Path | Bytes | Weights | Context | Functions | Status |
|---|---|---|---|---|---|
aff4/lfm2_5_8b_a1b_decode_aff4_gather_mf64_d2 |
5,724,920,429 | int4 affine experts + int8 | 16384 | main + prefill | SHIP |
mf64/lfm2_5_8b_a1b_decode_sym8_gather_mf64 |
9,447,767,758 | int8 sym8 |
16384 | main + prefill | SUPERSEDED |
mf64-tp/lfm2_5_8b_a1b_decode_sym8_gather_mf64_tp |
9,447,998,621 | int8 sym8 |
16384 | main + prefill | RETIRED |
Each folder holds <name>.aimodel/ (main.mlirb, main.hash, asset metadata.json), a
bundle-level metadata.json, and tokenizer/ (tokenizer.json, tokenizer_config.json,
chat_template.jinja).
aff4/README.md sits beside the bundle directory rather than inside it, deliberately: a
downloader that verifies a bundle by summing its subtree β as this project's does β counts a
repo card inside the bundle folder as 1,084 unexpected bytes and refuses. Keep repo prose one
level above any bundle directory.
Stop token: eos_token = "<|im_end|>" in all three bundles.
Chat template. LFM2.5-8B-A1B is an always-thinking model, so the generation prompt
terminates the reasoning block β {{- "<|im_start|>assistant\n<think></think>\n" -}}, the
same form the LFM2.5-2.6B bundles use. Free-form generation therefore returns text with the
bundle exactly as published rather than spending the whole budget inside an unterminated
<think> block. Guided decoding is unaffected either way, because the grammar forces
structured output from the first token.
Provenance
| Base checkpoint | LiquidAI/LFM2.5-8B-A1B |
| Zoo recipe | models/lfm2.5-8b-a1b-moe/recipe.toml β ["lfm2.5-8b-a1b"], status = "verified" β the lineage mf64/ reproduces |
| Recipe | export_lfm2_moe_multifunction.py β metalize_moe_batched installs BatchedMetalSwitchGLU |
| Toolchain base | apple/coreai-models @ b1cb71b8522d99408059fa0b98b8742171bcb0b8 + the coreai-model-zoo python overlay |
| Producer fingerprint | coreai-core 1.0.0b2 on every inner asset metadata.json |
| Vocab | 128,000 |
| Export functions | main (S=1 decode) + prefill (S=64) |
BatchedMetalSwitchGLU serves S=64 by sorting (token, expert) pairs and falls back to the
q=1 gather path at S=1, so both entrypoints keep the gather kernel and decode arithmetic is
identical between them.
aff4/ β the D2 rebuild
D2 is mf64/'s weights re-quantized and re-exported under three changes, each priced
separately against an fp32 oracle before it was taken:
| lever | what it does |
|---|---|
aff4 experts |
routed experts at affine int4, per-(output row, K-block 64) scale + bias, MSE-optimal clip. The 0.43 GiB of weight saving. |
| L1 β untied, quantized head | lm_head untied from embed_tokens and quantized at int8 symmetric (absmax). |
| L2 β fp16 attention projections | fp32_attn_proj=False, so the attention matmul is fp16 and the compiler's materialised transposes follow it. |
The head's quantization scheme is the whole story of this bundle. An affine head (D1)
compiled to a 1,457,034,236-byte graph constant; the symmetric head (D2) compiled to
408,458,282. Same weights, same size on disk, one field different. A symmetric dequantize
is a scale multiply the delegate absorbs into the matmul; an affine one is (q β zp) Γ scale,
and the subtract is not absorbed β so the folder precomputes two half-gigabyte dequantised
2048x128000 fp16 transposes of the head instead. D1 paid for its head three times. The
lever is not "quantize the head", it is "give the head a dequantize the delegate can absorb".
Non-expert weights outside the head are the shipped int8 per-block-32 recipe calibrated on
the decode spec. L3 β dropping the prefill entrypoint β was not taken: it is worth
0.27 GiB on this bundle and it would put every prompt token through the S=1 graph.
Not published, deliberately: a non-QAT int4 int4km-gather variant exists upstream and
does not hold quality on this model.
Requirements
- Apple silicon Mac, Core AI runtime. Mac-only β neither bundle fits an iPhone even with the memory-limit entitlement.
- Engine contract: 2 inputs (
input_ids,position_ids) β logits. No static inputs, no per-step mask. Runs on the pipelined engine and on the logits-capable sequential engine, which is what grammar-constrained decoding needs. - Manifest context 16,384;
maxOutputTokensand the S=64 prefill chunk are read from the bundle's ownmetadata.json(function_map: {"main": ["main", "prefill"]}). - The runtime wires the whole weight set at the first forward pass, not at load β an A1B is charged for all 8.3B parameters resident. "1.5B active" is a decode-speed property and nothing else. Budget by the numbers in Measurements, not by active parameter count.
- Minimum practical machine memory: 16 GB for
aff4/, with the machine reasonably quiet.mf64/does not start on 16 GB; 24 GB or more. - Disk: the bundle plus its compiled graph cache on first load.
aff4/compiles to 6,115,467,248 B;mf64/to 10.374 GiB. - The bundle manifest declares
runtime_env COREAI_CHUNK_THRESHOLD = "1", the most memory-conservative prefill setting available. Keep it.
Measurements
All figures below are from a 16 GB Apple silicon Mac (M2 Pro, macOS 27 beta), under an external watchdog sampling wired memory, reclaimable memory and the compressor. Compile-only figures are zero-prompt runs with no forward pass.
aff4/ (D2) β the completed leg
Eleven real report questions, guided JSON-constrained decoding against a fixed schema, greedy, both kill criteria armed for the whole run and verified alive.
| value | |
|---|---|
| answered | 11 / 11, every one parsing |
| mean decode | 56.23 tok/s (range 54.17β60.05, no decay across the leg) |
| load | 25.46 s (warm compile cache) |
| campaign wall | 318.79 s |
| wired peak / growth | 9.16 / 6.22 GiB |
| reclaimable trough | 2.45 GiB against a 1.25 GiB floor β 1.20 GiB of margin, never approached |
| watchdog | never tripped |
Two later runs on the same bundle read 55.81 and 59.81 tok/s, so 56.23 sits in the middle of a narrow band rather than at a lucky edge.
Throughput is flat in prompt length β question 11 ran at 55.12 tok/s against question 1's 59.17, and the difference tracks prompt length rather than elapsed time. Across 521 samples in an earlier leg the longest prompt produced the smallest boundary spike. That flatness is an LFM-MoE property; the dense Gemma 4 12B measured on the same machine does not have it.
Quality on that leg, counted by an automated scorer with every flag hand-adjudicated:
| measure | result |
|---|---|
| JSON parses | 11 / 11 |
| inversion (someone else's words attributed to the subject, or the reverse) | 0 / 11 |
| invented attributor | 0 / 11 |
| unsupported motive | 0 / 11 |
| narration | 2 / 11 non-zero, both low |
| third-person voice | 6 flagged β 5 upheld |
| schema echo | 3 flagged β 1 upheld |
Zero inversions across eleven questions is the headline. One of the eleven answers is bad in a way that matters, and it is recorded rather than averaged away: on a "red flags" question the model recited the question's own enumerated category list back as findings, manufacturing a serious accusation out of the prompt's taxonomy. Ten of eleven are sound; that one is a different order of defect from an untidy one.
Release and re-acquire. Three teardown cycles: wired at peak stable to 0.012 GiB across cycles; wired after release settles to within 0.075 GiB of baseline; warm re-acquire 0.004β0.006 s; output byte-identical across cycles, so no state leaks across a release.
Loading does not wire. After a 116.28 s cold-cache load the wired counter read β0.031 GiB against baseline β the weights are read, and the wiring burst happens at the first forward pass.
The requirement, and why mf64/ is superseded
Compile-only, one at a time, watchdog armed:
aff4/ (D2) |
mf64/ (sym8) |
|
|---|---|---|
| bundle on disk | 5.33 GiB | 8.799 GiB |
| compiled blob | 5.695 GiB | 10.374 GiB |
| graph constant | 0.380 GiB | 1.591 GiB |
| blob Γ· bundle | 1.07Γ | 1.18Γ |
peak phys_footprint during compile |
0.39 GiB | β |
forecast peak (blob Γ 1.106) |
6.30 GiB | 11.473 GiB |
| requirement on a 16 GB Mac (peak + 1.25 GiB in-flight floor) | 7.55 GiB | 12.72 GiB |
| measured requirement, from the completed run | 7.87 GiB | never completed a run |
This machine's ordinary reclaimable memory is 4.5β7.3 GiB, with a single best-ever reading
of 9.97 GiB that has never repeated. 12.72 GiB is above anything it has ever had free.
sym8 is not a slow model here; it is a model that cannot start β and the whole 4.2Γ gap
between the two graph constants is compiler-materialised copies that were never weights.
Two honest notes against ourselves:
- The shipped gate is 7.55, the completed run says 7.87. The blob law is 1.3% conservative on wired growth; the 0.32 GiB gap is the dirty footprint, which a ratio over a compiled blob has no term for. A finished measurement raised this model's requirement.
- D2's exact stack was never itself an oracle row. Its components were, separately, and
both are free: against an fp32 oracle (41 headline positions, 328 aggregate) the
aff4base scores 4/41 and 79/328;+ head int8 symmetricscores 4/41, 79/328;+ fp16 attentionscores 4/41, 78/328. For comparisonsym8scores 1/41, 14/328 and fp16 scores 0/41, 12/328 β sosym8is the more faithful quantization, and it is not the one that runs. That trade is the reason this repo keeps both.
mf64/ (sym8) β kept as evidence
Measured before it was superseded, 10-sample guided harness, sequential engine:
| Guided JSON parse / enum-clean | 10/10 / 10/10 |
| s/row, long / short | 7.70 / 4.71 |
| Decode, guided | 51.9β53.5 tok/s |
| Prefill | 6.3 ms/token, linear in depth at 8k and 15k |
| Needle-in-haystack | 3/3 at 8k, 3/3 at 15k (14,566 prompt tokens, TTFT 91.41 s) |
| Max RSS | 9.04β10.73 GB |
| Cold load / warm load | 60.6 s / 22.9β26.2 s |
Read Max RSS carefully: a resident set counts clean mapped pages the kernel can drop for
free, so it was never a statement about what the machine has to give up. The wired figures
above are.
mf64-tp/ β RETIRED
Failed its Mac gate on 2026-08-18. Do not deploy it and do not route traffic to it.
A token-tiled prefill kernel. Three headline claims, two failed. (1) The Metal kernel compiles
and runs β passed. (2) It is not bit-identical to mf64: 3 of 10 guided rows diverge,
including one that invents an entity and one that flips a schema enum, while a same-session
mf64 re-run reproduced its own outputs byte for byte on all 10 rows. Both bundles parse
10/10 and are enum-clean, so a schema-shape gate would not have caught this. (3) Prefill
came in at 5.35 ms/prompt-token against a prediction of 0.97 β a 1.17Γ speedup, not 6.5Γ.
Decode is genuinely unchanged, confirming the q=1 fallthrough is clean.
Usage
Swift Package Manager, via CoreAIKit β a community package, not affiliated with Apple, requiring macOS 27 beta:
.package(url: "https://github.com/john-rocky/coreai-kit", branch: "main")
// target dependency: .product(name: "CoreAIKit", package: "coreai-kit")
ModelID addresses a bundle as repo + path + revision, where path is the subtree in this
repo holding one complete bundle (metadata.json + *.aimodel/ + tokenizer/). It downloads
from the Hub on first use and is cached afterwards:
import CoreAIKit
let model = ModelID(
"visible-cx/LFM2.5-8B-A1B-CoreAI",
path: "aff4/lfm2_5_8b_a1b_decode_aff4_gather_mf64_d2",
revision: "c9b541e85b9125ecb9dc7f4fa6d3e3c06ccd78e5") // pin, don't ride main
var config = ChatSession.Configuration()
config.engineVariant = .sequential // required for guided / grammar-constrained decoding
config.temperature = nil // greedy
let chat = try await ChatSession(model: model, configuration: config)
for try await event in chat.streamResponse(to: "β¦") {
if case .response(let delta) = event { print(delta, terminator: "") }
}
ChatSession(bundleAt:) loads a bundle directory already on disk, which is the path to use if
you stage the bundle yourself. Leave COREAI_CHUNK_THRESHOLD alone β the manifest sets it.
Digests
aff4/lfm2_5_8b_a1b_decode_aff4_gather_mf64_d2, computed from the exact published bytes.
main.hash is the raw 32 bytes of sha256(main.mlirb) β verified, not assumed.
| path (bundle-relative) | bytes | sha256 |
|---|---|---|
metadata.json |
634 | 8651c35cb26fb0e2cc80eb1ce952efe170bfa5a074b99fe04ad4fae87e58fbdd |
β¦.aimodel/main.hash |
32 | bb97d0dd52c410366f23281534f252dc0f5dad42450c03df28a4be2f8cf299c4 |
β¦.aimodel/main.mlirb |
5,707,008,966 | ad57774b86dae371be1318265bfd2aa41cc315ee961efd3ad14820ca6cd8a775 |
β¦.aimodel/metadata.json |
105 | 4a78efbbae885ceb04bd9503fb6eded741ce22c77075157c8e7e863059b8341c |
tokenizer/chat_template.jinja |
4,779 | b2881deb506bdff56bf6ac46aaa33292c119953f82fedaf276634fcc4282a559 |
tokenizer/tokenizer.json |
17,905,598 | 695be7802a0e4b8a81048f0ff5ebb7fc811a0ba5a6be63dbb24deb5a81096f41 |
tokenizer/tokenizer_config.json |
315 | b53c3caceb7fa0de424e1aa67b3cc5008cd2f8b8d2426a8fa221ef3cf1a949d5 |
| total | 5,724,920,429 |
Why per-file digests and not just a revision. A revision pin is an assertion about a repository; this is an assertion about bytes, and only the second survives a mirror, a re-upload or a CDN edge that lies. Hugging Face stores LFS objects under their SHA-256 and everything else under its git blob oid, so the whole subtree above can be checked against this table with zero bytes downloaded, from the tree API alone.
This bundle has had three addresses in one day β staged locally, published at 9e023bafβ¦,
republished at c9b541e8β¦ when a stray README moved out of the bundle subtree β and
ad57774b86dae371β¦ did not move once. Uploading bytes does not rehash them.
Core AI .aimodel bundles are not byte-reproducible: the exporter is not deterministic
even against itself. Integrity rests on these digests, not on a rebuild.
Status
| Artifact | Status |
|---|---|
aff4/β¦_aff4_gather_mf64_d2 |
SHIP. Measured: 11/11 answered and parsed at 56.23 tok/s on a completed watchdogged leg, 0/11 inversions, wired growth 6.22 GiB, release returns to baseline within 0.075 GiB. Requirement 7.55β7.87 GiB on a 16 GB Mac. |
mf64/β¦_sym8_gather_mf64 |
SUPERSEDED β kept as evidence, not deleted. Measured: 10/10 guided parse and enum-clean, 51.9β53.5 tok/s, needle 3/3 at 8k and 15k. It requires 12.72 GiB to start, which is above anything the 16 GB reference machine has been measured to have free. Nothing substitutes it for aff4/ and nothing should. |
mf64-tp/β¦_sym8_gather_mf64_tp |
RETIRED β failed its Mac gate. Kept for the record only. |
No PSNR or numerics gate has been run against a PyTorch reference on device for any bundle here. The oracle rows quoted above are box-side torch fp32 comparisons; qualification on the Mac is behavioural (parse rate, enum conformance, grounding scoring, clean stop, needle recall) plus the memory instrumentation.
License
LiquidAI/LFM2.5-8B-A1B is released under the LFM Open License v1.0 (lfm1.0), and
upstream declares it as license: other + license_name: lfm1.0. These bundles are a
derivative of that checkpoint and the same licence and obligations travel with them β see the
upstream licence.
Redistribute the licence with the files. The contribution here is the conversion, the
compression recipe and the qualification evidence, not the weights.