Downloads · 30 days
45
42% of all-time downloads
okayuji/gemma-4-12b-it-coreml-128k
gemma-4-12b-it-coreml-128k is a text generation model from okayuji. Use it when you need the model to write or continue text. The card lists the license as apache-2.0.
Run Google's Gemma 4 12B IT locally on an Apple silicon Mac with Core ML. One int4 bundle supports conversations up to 131,072 tokens, switching from its 32K mode to its 128K mode without reloading weights or copying…
Downloads · 30 days
45
42% of all-time downloads
All-time downloads
108
Public
Repo size
10.1 GB
Likes
1
Public
Click a slice to open those files.
.bin10.9 GB · 100%
From the Hugging Face model README
Run Google's Gemma 4 12B IT locally on an Apple silicon Mac with Core ML. One int4 bundle supports conversations up to 131,072 tokens, switching from its 32K mode to its 128K mode without reloading weights or copying the conversation cache.
Swift runtime & app · Model guide · Watch the X demo · Article: English / Japanese
MLState.Requires: macOS 26+ · Xcode 26 / Swift 6.2 · ~11 GB disk · ~12 GB inference memory, 24 GB+ recommended. See Requirements for measured peaks.
Use the published companion CLI:
git clone https://github.com/oka-yuji/coreml-llm-samples.git
cd coreml-llm-samples
./scripts/download-model.sh
swift run -c release --package-path CoreLLMKit corellm-chat --model ./models/gemma-4-12b-it-coreml-128k --stats
The script downloads this bundle to ./models/gemma-4-12b-it-coreml-128k. The model guide also covers one-shot prompts and CLI flags. For the app, open CoreMLSamples.xcodeproj at the repository root and run the CoreMLSamples scheme on My Mac.
For a standalone download, the existing Hub command is:
hf download okayuji/gemma-4-12b-it-coreml-128k --local-dir ./gemma-4-12b-it-coreml-128k
The runtime supplies the tokenizer, prompt template, host inputs and speculative decoding. The Swift ladder example below shows the shared-state mechanism; it is intentionally only a partial example.
M4 Max (128 GB) / macOS 26.5.2, CPU_AND_GPU, greedy decode, 32 prompt tokens (BOS + template),
one process per run, via the companion CLI.
Prompt (Japanese — "What is the capital of Japan? Answer concisely, with one sentence of reasoning."):
日本の首都はどこですか?理由も一文添えて簡潔に答えてください。
Output:
日本の首都は東京です。政治、経済、文化の中心地として機能しているためです。
("The capital of Japan is Tokyo. It serves as the country's political, economic and cultural center.")
| Run | TTFT | Decode | Draft acceptance | Peak memory |
|---|---|---|---|---|
| Speculative decoding ON | 3.83 s | 62.3 ms/tok (16.0 tok/s) | 0.75 | 20.50 GB |
| Speculative decoding OFF | 0.26 s | 98.3 ms/tok (10.2 tok/s) | — | 18.00 GB |
The two runs produced byte-identical output — that is what "lossless speculation" means here: only the speed changes, never the text. The speculative run's larger TTFT is a one-time cost inside its first prefill, not a per-token cost.
Measured on an M4 Max (128 GB) / macOS 26.5.2, coremltools 9.0, compute units CPU_AND_GPU,
single-token decode (S=1), one process per condition.
| Metric | Value | Notes |
|---|---|---|
Decode — ctx32k mode | 90.51 ms/tok (~11 tok/s) | ladder function, ≤32K context |
Decode — ctx128k mode | 300.96 ms/tok (~3.3 tok/s) | ladder function, >32K context |
| Context length | 131,072 tokens | full-attention layers; sliding layers ring at 1024 |
| KV cache size (full) | ~2.48 GB | full 8 layers 2.147 GB + sliding 40 layers 0.336 GB; fill-independent (same at 1 token or 128K) |
| MTP speculative decode | ×1.05 – ×1.47, lossless | draft_len=4, regime-switch drafter; output identical with speculation off |
| Copy-zero promotion (32K → 128K) | bit-exact; function switch on the same state | shared MLState; no re-load, no KV copy |
| Quantization | int4 matmul (AWQ p999) + int8 lm_head | block size 32 |
| Core ML model bundle | 6.71 GiB | 4 chunks + lm_head; two functions share one weight set |
The speculative-decode multipliers span the range ×1.05 (verbatim) → ×1.27 (quoting) → ×1.47 (enumeration), all lossless, and match the standalone 32K bundle to within ±0.03.
Timing clarification (2026-10-23): the earlier card attached ~0.64 s to the copy-zero promotion row.
That value belongs to the 2026-07-13 copy-based prototype (state mapping plus write_state),
not the shared-state bundle shipped here. The 2026-07-15 multifunction build switches functions
without state export/import; its benchmark found no measurable additional switch cost.
The old timing remains a result for the earlier design, not a latency claim for this bundle.
Benchmark sources: the author's 2026-07-15-phasec-c0v1-build.md (decode and shared-state gates),
2026-07-13-phasec-c0-ctx-ladder.md (copy-based prototype), and
2026-07-19-12b-ladder-drafter-regime.md (speculative speedups). These lab records are not public;
the public model guide
summarizes the benchmark conditions, and the verification guide
describes the equality checks.
The bundle is an MLProgram + MLState graph that co-hosts two functions — ctx32k and
ctx128k — in one weight set. The full-attention KV state is declared once at the maximum width
[1, 131072, 512] and shared by both functions, so moving from the 32K to the 128K regime is a
copy-zero promotion: the same MLState buffer is simply read by a wider function. Promotion is
bit-for-bit lossless (the zero-padded KV slots are masked with -inf, contributing exactly 0 to
the softmax), so decode output is identical before and after promotion.
The result: short and medium conversations (≤32K tokens) decode at the fast 32K speed, and the model transparently steps up to 128K only when the context actually exceeds 32K — without a re-load and without a KV copy.
A single weight set exposes two Core ML
functions driven by one shared MLState. The shared-state mechanism, in Swift:
import CoreML
// 1. Compile once. Each .mlpackage becomes one .mlmodelc containing BOTH functions.
// Measured: ~1.2 s for the whole bundle on an M4 Max (~0.3 s per chunk). Cache and reuse it.
let compiled = try await MLModel.compileModel(at: bundleURL.appending(path: "chunk_0_12.mlpackage"))
// 2. Open the same .mlmodelc twice, once per function. No recompile, weights are deduplicated.
func open(_ function: String) async throws -> MLModel {
let cfg = MLModelConfiguration()
cfg.computeUnits = .cpuAndGPU // required: the KV MLState is GPU-resident
cfg.functionName = function // "ctx32k" | "ctx128k"
return try await MLModel.load(contentsOf: compiled, configuration: cfg)
}
let chunk32 = try await open("ctx32k")
let chunk128 = try await open("ctx128k")
// 3. ONE state, made from either model — it is physically [1, 131072, 512] in both cases.
let state = chunk32.makeState()
// 4. Decode through ctx32k; once the write position reaches 32768, decode through ctx128k
// instead. Same `state` object, no read_state/write_state — that is the copy-zero promotion.
let model = (position < 32768) ? chunk32 : chunk128
let out = try model.prediction(from: inputs, using: state)
inputs above is elided on purpose: the host-side features (one-hot write slots, RoPE tables,
sliding/full attention masks) are built by the companion package, and this card does not print code
that has not been run. The snippet through step 3 is the verified part — compile, both
functionName loads, and the shared state.
From manifest.json (promptPrefix / promptSuffix). These are not the stock Gemma
<start_of_turn> markers — a loader that assumes them will silently produce a different token
sequence. A single turn is BOS + promptPrefix + user text + promptSuffix:
<bos><|turn>user
{user text}<turn|>
<|turn>model
<|channel>thought
<channel|>
The model continues from there. For multi-turn, close the assistant's text with <turn|>\n and
append the next user turn with the same prefix/suffix:
<bos><|turn>user
{turn 1 user}<turn|>
<|turn>model
<|channel>thought
<channel|>{turn 1 model}<turn|>
<|turn>user
{turn 2 user}<turn|>
<|turn>model
<|channel>thought
<channel|>
The exact strings, verbatim from manifest.json (no trailing newline after <channel|>):
"promptPrefix": "<|turn>user\n",
"promptSuffix": "<turn|>\n<|turn>model\n<|channel>thought\n<channel|>"
Use tokenizer.json from this repository, and prepend BOS if the tokenizer does not. With BOS, the
sample prompt shown earlier encodes to 32 tokens.
Total download ~10.2 GiB (~11 GB). The four chunk_*.mlpackage folders plus lmhead.mlpackage
are the 6.71 GiB Core ML model bundle; the drafters, embedding table, and tokenizer account for
the remaining ~3.5 GiB.
Size units corrected on 2026-10-23: the earlier card labeled these binary estimates as GB/MB.
The Hub file inventory at revision 40488bbd72275753c25a80b009c25882cc49a6b0 contains
10,945,351,416 bytes of assets (excluding README.md and .gitattributes), including
7,207,132,566 bytes for the chunks and LM head. File sizes below use GiB/MiB; RAM observations
retain the units of their original measurements.
| File | Role | Size |
|---|---|---|
chunk_0_12.mlpackage | Transformer layers 0–11 (int4, MLProgram + MLState) | ~1.4 GiB |
chunk_12_24.mlpackage | Transformer layers 12–23 | ~1.4 GiB |
chunk_24_36.mlpackage | Transformer layers 24–35 | ~1.4 GiB |
chunk_36_48.mlpackage | Transformer layers 36–47 | ~1.4 GiB |
lmhead.mlpackage | LM head / vocabulary projection (int8) | ~1.0 GiB |
drafter_ring.mlmodelc | MTP draft model, 131072-width (used in the ctx128k regime) | ~807 MiB |
drafter_ring32k.mlmodelc | MTP draft model, 32768-width (used in the ctx32k regime) | ~807 MiB |
embed_fp16.bin | Token embedding table, fp16, shape [262144, 3840] | ~1.9 GiB |
tokenizer.json | Tokenizer | ~31 MiB |
tokenizer_config.json | Tokenizer configuration | ~2 KB |
manifest.json | Bundle manifest (architecture, context length, prompt template) | <1 KB |
convert_config_ladder.json | Chain config (tensor shapes, quant recipe, ladder function windows) | ~18 KB |
The chunks and lm_head are shipped as .mlpackage only. The Swift loader compiles them to
.mlmodelc on first load and caches the result, so no pre-compiled .mlmodelc copies are included.
The two drafters are shipped as .mlmodelc (the loader detects those directly).
CPU_AND_GPU..mlpackage is compiled to .mlmodelc on first use
and cached to disk; subsequent loads are warm. This step is cheap: ~1.2 s for the whole bundle
on an M4 Max (about 0.3 s per chunk).~/Library/Caches/com.apple.e5rt.e5bundlecache) grows by several GB per run. Deleting it is safe
and recovers the space; it regenerates, and the next load is just slower.drafter_ring.mlmodelc /
drafter_ring32k.mlmodelc were not found next to manifest.json. Re-run the download.manifest.json and
convert_config_ladder.json describe the chain, prompt template, and ladder functions.MLState; CPU-only and ANE
configurations are not usable with multiple MLState instances. There is no iOS build.These gates are listed because "it runs" and "it computes the same thing as the reference" are different claims, and this bundle makes the second one.
Correctness was gated against the PyTorch (Hugging Face) reference at the bit level. The 128K
conversion passed all 13 correctness gates (RoPE range at pos 131071, schema/runtime,
self-consistency, cross-generation int4/fp16 exact matches, ring-boundary, deep-feed to 40960); the
Context Ladder passed all four ladder gates exact, including the copy-zero promotion gate (feed
2000 tokens in ctx32k, switch functions on the same state with zero read/write, then match the
ctx128k reference greedy output with #diff=0). The rare near-tie cases (fp16 summation-order
differences near the 32768 boundary) were attributed by an arbitration panel and never produced a
hard failure — every near-tie had the oracle token within the chain's top-2.
This bundle runs with the Core ML runtime in coreml-llm-samples. For separate Core AI / Foundation Models conversions and examples, see coreai-samples. That project is a related collection for comparing the frameworks; its runtime uses different model assets and does not load this Core ML bundle.
google/gemma-4-12B-it. The
artifacts here are derived from Gemma 4 12B IT: the original files have been modified — the
weights converted to a Core ML graph and quantized (int4 AWQ matmul / int8 lm_head).