Views
No views yet
coreai-torch (LLMs: coreai.llm.export) into .aimodel bundles that run on the GPU or the Neural Engine, e.g. Qwen3-8B 4-bit decodes at 94 tok/s on an M4 Max GPU, MLX 90 under the same protocol (apple-silicon-llm-bench, macOS 27 beta, 2026-06).nanbeige-3b, int8hu bundle ·
data)[!NOTE] Update 2026-07-17:gpu-pipelined-b2/adds thenanbeige4_1_3b_decode_int8hu_block32_symbundle re-exported withcoreai-core 1.0.0b2(loadable on the OS 27 beta 3 toolchain; the original b1 tree is retained). This is the exact artifact measured and iPhone-gated on DeviceMark (PB nat 24/24 · oracle 24/24, iOS 24A5380h).
model_type: "llama",
3.93B total / ~3B non-embedding backbone). Source: Nanbeige/Nanbeige4.1-3B (Apache-2.0). A reasoning /
agentic model whose first-party card claims it beats Qwen3-4B and rivals Qwen3-32B / Qwen3-30B-A3B
(LiveCodeBench-Pro-Easy 81.4 vs 40.2, AIME 2026-I 87.4, GPQA 83.8) — a 32B-class reasoner at 3.93B,
running on an iPhone..aimodel bundle (ready to run):
mlboydaisuke/Nanbeige4.1-3B-CoreAI —
gpu-pipelined/nanbeige4_1_3b_decode_int8hu_block32_sym_s1/ (full LanguageBundle incl. tokenizer).qwen3.py MINUS the q/k-norm (qwen3 already has a bias-free fused QKV), so the body is the
existing overlay with one norm removed — see models/macos/llama.py. Pure-attention, KV-only state
(no conv / recurrent), so it needs no engine patch beyond the base stack.nanbeige-3b, int8hu bundle ·
data)import CoreAIOps; no session, no model plumbing, downloads on first use):let tldr = try await CoreAI.summarize(text, options: .model("nanbeige4.1-3b"))1git clone https://github.com/john-rocky/coreai-kit
2open coreai-kit/Examples/ChatDemo/ChatDemo.xcodeproj
3# → Run, then pick "Nanbeige4.1 3B" in the model picker
4
5# agents / headless (macOS):
6cd coreai-kit/Examples/ChatDemo
7swift run chat-cli --model nanbeige4.1-3b --prompt "What can you do, offline?"1import CoreAIKit
2
3let chat = try await ChatSession(catalog: "nanbeige4.1-3b")
4let reply = try await chat.respond(to: prompt)
5// reply: the answer, generated fully on-deviceKitLanguageModel plugs this bundle into the same system LanguageModelSession; your Tools, @Generable types and transcripts work unchanged, and capabilities (tool calling, guided generation) auto-detect per model.Examples/ChatDemo/Sources/QuickStart.swift
— this exact code as one typed function, no UI; the CLI is an argument shell over it, and
the GUI drives the same ChatSession across turns for its transcript.
Multi-turn? Hold the ChatSession and call respond(to:) per turn — it keeps the
conversation history; streamResponse(to:) yields tokens as they decode.https://github.com/john-rocky/coreai-kit → product CoreAIKitcom.apple.developer.kernel.increased-memory-limitdownloadProgress callback)COREAI_CHUNK_THRESHOLD=1)| config | bundle | prefill tok/s | decode tok/s | numerics |
|---|---|---|---|---|
| int8hu --head-sym (ship), M4 Max | 4.3 GB | 114.9 | 114.5 | engine ≡ fp32-HF oracle (raw greedy → "Paris"); reasoning coherent (trick "17 sheep, all but 9" → reasons to 9) |
int8hu --head-sym --static-ids (_s1, ship), iPhone 17 Pro | 4.3 GB | 16.6 | 15.9 | nat 24/24 + oracle 24/24 — token-identical to the M4 Max GPU reference (Paris / Tokyo + full continuation) |
engine ready 53.5 s, device free 51 GB, no
jetsam / no std::bad_alloc — the largest bundle we have run on the pipelined bench (4.58 GB payload).--static-ids is REQUIRED for the device. The generic dynamic-input_ids export is fast on the
Mac but on the iPhone pipelined engine (chunkThreshold=1, every step S=1) it pays a per-step
input_ids re-specialization that is pathological on a 4.3 GB model (~37 s/step cold; the 900 s
probe never finished the first 24-token run). Fixing input_ids at [1,1] (the qwen3.5 loop-free
device pattern; --static-ids → _s1 bundle) eliminates it — chunkThreshold=1 feeds S=1 anyway, so
no prefill loss — and the device numerics complete 24/24.--head-sym,
plain symmetric). symmetric_with_clipping craters big-vocab heads (the documented qwen lever).int4hu (body int4 per-block-32 + int8 head) is 2.9 GB and 169 tok/s on the Mac, and its raw
single-token greedy still returns "Paris" — but multi-token reasoning CRATERS: the same
"17 sheep, all but 9 run away" trick collapses to a wrong "17" with a repetition loop and Chinese
drift. The single-token probe is misleading for a reasoning model — you must check multi-token.
This is the non-QAT-int4 structural cliff (same wall as qwen3.5 / LFM2.5; needs QAT). Palettized
(k-means) int4 does not rescue it either — for non-QAT weights the cliff is the scheme-independent
property, and on the GPU-pipelined path the LUT dequant is slower than linear besides. int8hu ships.LlamaForCausalLM oracle, no trust_remote_code): teacher-forced
top-1 24/24, cosine 1.000000, max-abs-logit Δ = 0 (_smoke/test_nanbeige_parity.py, USE_HF_IMPL=true)._smoke/gen_nanbeige_device_ref_tokens.py). Reasoning models drift on a bare prompt after the
answer — the first token is the anchor (Paris 9965 / Tokyo 20150) and the full 24 still matched here.1cd coreai-models # with the plain-Llama overlay (models/macos/llama.py) in place
2# device ship (REQUIRED static [1,1] for fast iPhone decode):
3.venv/bin/python ../coreai-models-community/conversion/export_nanbeige41_decode_pipelined.py \
4 int8hu --head-sym --static-ids
5COREAI_CHUNK_THRESHOLD=1 ./.build/out/Products/Release/llm-benchmark \
6 --model exports/nanbeige4_1_3b_decode_int8hu_block32_sym_s1 -p 128 -g 256 -n 3COREAI_CHUNK_THRESHOLD=1 before engine creation; the bundle's input_ids is static
[1,1], so every prefill token is fed as an S=1 step (never call engine.warmup() — warm with a
1-token generate; llm-runner needs --warmup exact --warmup-length 1).