Views
No views yet
🆕 Multimodal version available — for text + image + video + audio, usemlboydaisuke/gemma-4-E4B-multimodal-coreml. Same Gemma 4 E4B decoder + ANE-targeted vision encoder + Conformer audio encoder. Validated 2026-05-03 on iPhone 17 Pro at 15.7 tok/s with all four input modalities working. This text-only repo stays available for users who don't need vision/audio.
Package.swift:1.package(url: "https://github.com/john-rocky/CoreML-LLM", branch: "main"),
2
3// In your target:
4.product(name: "CoreMLLLM", package: "CoreML-LLM"),1import CoreMLLLM
2
3// First call pulls the bundle from this repo to Documents/Models/.
4// Subsequent calls reuse the on-disk copy.
5let llm = try await CoreMLLLM.load(repo: "mlboydaisuke/gemma-4-E4B-coreml")
6
7let stream = try await llm.generate(
8 [CoreMLLLM.Message(role: .user, content: "Hello!")],
9 maxTokens: 256
10)
11for await chunk in stream {
12 print(chunk, terminator: "")
13}[CoreMLLLM.Message] array, append the
user/assistant turns, and pass the whole history to
generate(_:) again. Call llm.reset() to start a new
conversation (clears the KV cache).google/gemma-4-E4B-it (the 4B-effective Gemma 4 decoder), chunked into 4 sliding-window-attention pieces for Apple Neural Engine. Produced by john-rocky/CoreML-LLM via:python conversion/build_gemma4_bundle.py --model gemma4-e4b --ctx 2048chunk1.mlmodelc/ # L0–11 — INT4 palettized, owns its own KV
chunk2.mlmodelc/ # L12–23 — emits kv13_*/kv14_* aliases for producer L22/L23
chunk3.mlmodelc/ # L24–32 — KV-shared
chunk4.mlmodelc/ # L33–41 + lm_head — multi-function (decode_q1 + verify_qK)
embed_tokens_q8.bin 640 MB — INT8 token embeddings (262144 × 2560)
embed_tokens_scales.bin 512 KB
embed_tokens_per_layer_q8.bin 2.6 GB — INT8 per-layer embeddings (PLE)
embed_tokens_per_layer_scales.bin 512 KB
per_layer_projection.bin 53 MB — fp16 PLE projection
per_layer_norm_weight.bin 512 B — fp16 PLE norm
cos_full.npy / cos_sliding.npy 4 MB / 2 MB — precomputed RoPE cos
sin_full.npy / sin_sliding.npy 4 MB / 2 MB — precomputed RoPE sin
model_config.json 711 B — runtime config (used by the Swift app's loader)
hf_model/
├── tokenizer.json
├── tokenizer_config.json
├── config.json
└── generation_config.jsonkv13_* / kv14_* regardless of actual layer index, so the iOS side needs no model-specific wiring.model.mlpackage)?phys_footprint. We mmap the raw INT8 + scale .bin files instead, dequantize the few rows touched per token in pure Swift, and feed the result to the chunks. The chunks themselves are pure transformer bodies and stay ANE-resident..npy RoPE tables are pre-baked at conversion-time so Swift doesn't need to ship a cos/sin builder.hf_model/. If you prefer the upstream copy:1from transformers import AutoTokenizer
2tok = AutoTokenizer.from_pretrained("google/gemma-4-E4B-it")1from huggingface_hub import snapshot_download
2import coremltools as ct, numpy as np, json
3
4local = snapshot_download("mlboydaisuke/gemma-4-E4B-coreml")
5cfg = json.load(open(f"{local}/model_config.json"))
6chunks = [ct.models.MLModel(f"{local}/chunk{i}.mlmodelc")
7 for i in range(1, 5)]MLModelConfiguration.computeUnits = .cpuAndNeuralEngine will execute on ANE directly).Sources/CoreMLLLM/ChunkedEngine.swift — that is the canonical implementation; mirror it in Python by:embed_tokens_q8.bin (uint8) + embed_tokens_scales.bin (fp16) and dequantizing the row for the current token,embed_tokens_per_layer_q8.bin + embed_tokens_per_layer_scales.bin (per-layer rows, dequant on demand),chunk1..chunk4, threading kv* outputs from chunk2 as inputs to chunks 3–4 (KV alias names follow the producer-layer convention).CoreMLLLMChat model picker — it auto-downloads this repo and runs it via ChunkedEngine.| E2B | E4B | |
|---|---|---|
num_hidden_layers | 35 | 42 |
hidden_size | 1536 | 2560 |
num_key_value_heads | 1 | 2 |
intermediate_size | 6144 | 10240 |
num_kv_shared_layers | 20 | 18 |
| KV producers (sliding/full) | L13 / L14 | L22 / L23 |
| Chunk boundaries | L0-7, L8-14, L15-24, L25-34 | L0-11, L12-23, L24-32, L33-41 |
| Metric | Value |
|---|---|
| Decode tok/s | ~14 tok/s |
| Per-step latency | ~71 ms |
phys_footprint | ~4.5 GB |
| ANE placement | 100% |
--ctx 4096 (or higher) on a sufficiently large Mac to extend; the ANE rejects chunks whose declared context differs from model_config.json.