4
stars
24
commits
4
linked in READMEs
Sep 7, 2026
updated
Core AI is Apple's on-device ML runtime in iOS 27 / macOS 27 and the successor to Core ML: PyTorch models are exported with Apple's 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).
On-device vision-language model for iPhone / Apple Silicon. A Core AI port of
openbmb/MiniCPM-V-4.6 — the strongest
sub-2B open VLM — running fully local on the GPU via the Core AI pipelined engine:
pick a photo, ask about it, stream the answer.
Verified on iPhone 17 Pro: image → grounded answer at ~51.5 tok/s decode, all local.
Fridge photo → recipe ideas, fully on-device on an iPhone 17 Pro (CoreAIChat).
⚡ One line — run the kit's task op on this model
(import CoreAIOps; no session, no model plumbing, downloads on first use):
let caption = try await CoreAI.caption(imageAt: url, options: .model("minicpm-v-4.6"))
Every op, one shape — Cookbook.
▶️ Run it (source) — the VLChat runner (GUI + CLI, one app for every vision-language model in the catalog):
git clone https://github.com/john-rocky/coreai-kit
open coreai-kit/Examples/VLChat/VLChat.xcodeproj
# → Run, then pick "MiniCPM-V 4.6" in the model picker
# agents / headless (macOS):
cd coreai-kit/Examples/VLChat
swift run vlchat-cli --model minicpm-v-4.6 --image sample.jpg --prompt "What is in this image?"
💻 Build with it — complete; the glue is kit API, copy-paste runs:
import CoreAIKit
import FoundationModels
let vlm = try await KitVisionModel(catalog: "minicpm-v-4.6")
let session = LanguageModelSession(model: vlm)
let image = try ImageFile.load(imageURL) // any image file → CGImage + EXIF orientation
let reply = try await session.respond(to: Prompt {
prompt
Attachment(image.cgImage, orientation: image.orientation)
})
// reply.content: the answer about the image, generated fully on-device
The take-home is Examples/VLChat/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 KitVisionModel(catalog:) behind a LanguageModelSession.
Multi-turn about the same image? Hold the LanguageModelSession and call respond(to:)
per turn. The photo picker / file chooser is your app's own chrome — ImageFile.load
(kit API) turns any image file into model input.
Integration checklist
https://github.com/john-rocky/coreai-kit → product CoreAIKitNSPhotoLibraryUsageDescription — only if you use PhotosPickercom.apple.developer.kernel.increased-memory-limitdownloadProgress callback)MiniCPM-V-4.6 (1.3B) = a SigLIP So400m vision tower (980px / patch 14 / 27 layers, with a
window-attention insert-merger @ layer 6 + a downsample-MLP merger → ÷16 = 64 visual tokens per
448px slice) + a Qwen3.5-hybrid text backbone (qwen3_5_text: 0.8B, 24 layers, GatedDeltaNet
linear attention ×3 : full attention ×1, head_dim 256, vocab 248094, tied head). Connector =
2×2 spatial merges + MLP, spliced into the text embeddings at <image> positions (masked_scatter).
Recommended (optimized, 2026-06-25):
| path | what | dtype | size |
|---|---|---|---|
gpu-pipelined/minicpmv46_vlm_decode_int8hu/ | VLM text decoder (input_ids → logits + static image_embeds[64,1024]; in-graph gather ids ≥ V ? image_embeds[ids-V] : embed[ids]) | int8 body + untied int8 head | ~1.2 GB |
gpu-pipelined/minicpmv46_vision_int8lin/ | fixed-grid SigLIP vision encoder (pixel_values[1,3,448,448] → image_features[64,1024]) | int8 | ~0.6 GB |
The int8 head quantizes the big-vocab LM head (fp16 in int8lin = ~half the per-token read) → +48% decode
on iPhone 17 Pro (46→68 tok/s). The int8 vision halves the encoder's size (the encode is compute-bound, so
this is a size/memory win); pair it with a one-shot vision-graph warmup at load to hide the ~2.7 s first-photo
cold compile. Original …_int8lin decoder + fp16 minicpmv46_vision remain for compatibility.
The decoder is a complete qwen3.5-hybrid text LLM when image_embeds is zero — same bundle, no image needed.
The pipelined engine knows nothing about images. The whole multimodal state rides the
static-input hook (image_embeds buffer) + an id-space trick — the graph stays ids + positions → logits:
x/127.5−1) and writes
image_embeds [64,1024] into one owned MTLBuffer the engine binds on every step.<|image_pad|> ids are rewritten to extension ids V + slot (slot 0..63).
In-graph: embed = ids < V ? table[ids] : image_embeds[ids − V].Simpler than the Qwen3-VL port (no deepstack, no M-RoPE).
image_embeds every step, which dilutes the head gain; the text core
alone is 46 → 68 = +48%). ~64–70 tok/s in practice by device temperature. · M4 Max text core ~224 tok/s
(llm-benchmark), engine cold-spec ~2–4 s, ~1.5 GB resident (jetsam-safe).apps/CoreAIChat and the standalone MiniCPMVLM app have a MiniCPM-V 4.6 mode with a photo picker:
pick an image, ask, stream. The vision tower runs once per image (~hundreds of ms); each turn re-prefills (S=1).
Conversion + gates: see coreai-model-zoo / minicpm-v-4.6.
License: Apache-2.0 (inherited from openbmb/MiniCPM-V-4.6).
More models in this format: Core AI Model Zoo — 75 models, each with the recipe that produced it.
Want a different model on-device? Open a request — free, open weights only; the export and its measured numbers get published publicly.
24 commits
4
stars
24
commits
4
linked in READMEs
Sep 7, 2026
updated
Core AI is Apple's on-device ML runtime in iOS 27 / macOS 27 and the successor to Core ML: PyTorch models are exported with Apple's 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).
On-device vision-language model for iPhone / Apple Silicon. A Core AI port of
openbmb/MiniCPM-V-4.6 — the strongest
sub-2B open VLM — running fully local on the GPU via the Core AI pipelined engine:
pick a photo, ask about it, stream the answer.
Verified on iPhone 17 Pro: image → grounded answer at ~51.5 tok/s decode, all local.
Fridge photo → recipe ideas, fully on-device on an iPhone 17 Pro (CoreAIChat).
⚡ One line — run the kit's task op on this model
(import CoreAIOps; no session, no model plumbing, downloads on first use):
let caption = try await CoreAI.caption(imageAt: url, options: .model("minicpm-v-4.6"))
Every op, one shape — Cookbook.
▶️ Run it (source) — the VLChat runner (GUI + CLI, one app for every vision-language model in the catalog):
git clone https://github.com/john-rocky/coreai-kit
open coreai-kit/Examples/VLChat/VLChat.xcodeproj
# → Run, then pick "MiniCPM-V 4.6" in the model picker
# agents / headless (macOS):
cd coreai-kit/Examples/VLChat
swift run vlchat-cli --model minicpm-v-4.6 --image sample.jpg --prompt "What is in this image?"
💻 Build with it — complete; the glue is kit API, copy-paste runs:
import CoreAIKit
import FoundationModels
let vlm = try await KitVisionModel(catalog: "minicpm-v-4.6")
let session = LanguageModelSession(model: vlm)
let image = try ImageFile.load(imageURL) // any image file → CGImage + EXIF orientation
let reply = try await session.respond(to: Prompt {
prompt
Attachment(image.cgImage, orientation: image.orientation)
})
// reply.content: the answer about the image, generated fully on-device
The take-home is Examples/VLChat/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 KitVisionModel(catalog:) behind a LanguageModelSession.
Multi-turn about the same image? Hold the LanguageModelSession and call respond(to:)
per turn. The photo picker / file chooser is your app's own chrome — ImageFile.load
(kit API) turns any image file into model input.
Integration checklist
https://github.com/john-rocky/coreai-kit → product CoreAIKitNSPhotoLibraryUsageDescription — only if you use PhotosPickercom.apple.developer.kernel.increased-memory-limitdownloadProgress callback)MiniCPM-V-4.6 (1.3B) = a SigLIP So400m vision tower (980px / patch 14 / 27 layers, with a
window-attention insert-merger @ layer 6 + a downsample-MLP merger → ÷16 = 64 visual tokens per
448px slice) + a Qwen3.5-hybrid text backbone (qwen3_5_text: 0.8B, 24 layers, GatedDeltaNet
linear attention ×3 : full attention ×1, head_dim 256, vocab 248094, tied head). Connector =
2×2 spatial merges + MLP, spliced into the text embeddings at <image> positions (masked_scatter).
Recommended (optimized, 2026-06-25):
| path | what | dtype | size |
|---|---|---|---|
gpu-pipelined/minicpmv46_vlm_decode_int8hu/ | VLM text decoder (input_ids → logits + static image_embeds[64,1024]; in-graph gather ids ≥ V ? image_embeds[ids-V] : embed[ids]) | int8 body + untied int8 head | ~1.2 GB |
gpu-pipelined/minicpmv46_vision_int8lin/ | fixed-grid SigLIP vision encoder (pixel_values[1,3,448,448] → image_features[64,1024]) | int8 | ~0.6 GB |
The int8 head quantizes the big-vocab LM head (fp16 in int8lin = ~half the per-token read) → +48% decode
on iPhone 17 Pro (46→68 tok/s). The int8 vision halves the encoder's size (the encode is compute-bound, so
this is a size/memory win); pair it with a one-shot vision-graph warmup at load to hide the ~2.7 s first-photo
cold compile. Original …_int8lin decoder + fp16 minicpmv46_vision remain for compatibility.
The decoder is a complete qwen3.5-hybrid text LLM when image_embeds is zero — same bundle, no image needed.
The pipelined engine knows nothing about images. The whole multimodal state rides the
static-input hook (image_embeds buffer) + an id-space trick — the graph stays ids + positions → logits:
x/127.5−1) and writes
image_embeds [64,1024] into one owned MTLBuffer the engine binds on every step.<|image_pad|> ids are rewritten to extension ids V + slot (slot 0..63).
In-graph: embed = ids < V ? table[ids] : image_embeds[ids − V].Simpler than the Qwen3-VL port (no deepstack, no M-RoPE).
image_embeds every step, which dilutes the head gain; the text core
alone is 46 → 68 = +48%). ~64–70 tok/s in practice by device temperature. · M4 Max text core ~224 tok/s
(llm-benchmark), engine cold-spec ~2–4 s, ~1.5 GB resident (jetsam-safe).apps/CoreAIChat and the standalone MiniCPMVLM app have a MiniCPM-V 4.6 mode with a photo picker:
pick an image, ask, stream. The vision tower runs once per image (~hundreds of ms); each turn re-prefills (S=1).
Conversion + gates: see coreai-model-zoo / minicpm-v-4.6.
License: Apache-2.0 (inherited from openbmb/MiniCPM-V-4.6).
More models in this format: Core AI Model Zoo — 75 models, each with the recipe that produced it.
Want a different model on-device? Open a request — free, open weights only; the export and its measured numbers get published publicly.
24 commits