0
stars
8
commits
3
linked in READMEs
Aug 15, 2026
updated
Package.swift:
.package(url: "https://github.com/john-rocky/CoreML-LLM", branch: "main"),
// In your target:
.product(name: "CoreMLLLM", package: "CoreML-LLM"),
Platforms: iOS 18+ / macOS 15+.
import CoreMLLLM
let llm = try await CoreMLLLM.load(repo: "mlboydaisuke/qwen3-vl-2b-coreml")
let stream = try await llm.generate(
[CoreMLLLM.Message(role: .user, content: "Hello!")],
maxTokens: 256
)
for await chunk in stream { print(chunk, terminator: "") }
import CoreGraphics
let cgImage: CGImage = ... // your CGImage
let stream = try await llm.generate(
[CoreMLLLM.Message(role: .user,
content: "What's in this image?")],
image: cgImage,
maxTokens: 256
)
for await chunk in stream { print(chunk, terminator: "") }
Core ML port of Qwen/Qwen3-VL-2B-Instruct — text + vision, INT8 chunked, runs on iPhone A18 ANE.
Heads up: for new work prefer the stateful variant at
mlboydaisuke/qwen3-vl-2b-stateful-coreml— same model, but KV cache lives inside ANE viaMLState+slice_updateso memory is 6× lower (264 MB vs 1.7 GB) and decode is 2× faster (24 vs 10 tok/s) on iPhone 17 Pro. This recurrent repo is kept for backward compatibility with the v1.4.0 runtime.
qwen3_vl_2b_decode_chunks/
├── chunk_0.mlpackage # 353 MB — text path: embed + L0-6
├── chunk_1.mlpackage # 353 MB — L7-13
├── chunk_2.mlpackage # 353 MB — L14-20
├── chunk_3.mlpackage # 353 MB — L21-27
├── chunk_head.mlpackage # 311 MB — final_norm + lm_head + argmax
├── chunk_0_vision.mlpackage # 353 MB — chunk_0 with DeepStack injection
├── prefill_chunk_{0..3}.mlpackage # 353 MB each — T=32 batched prefill bodies
├── prefill_chunk_0_vision.mlpackage # vision-aware prefill chunk_0
└── embed_weight.bin # 622 MB — raw fp16 embed (151936 × 2048)
qwen3_vl_2b_vision/
└── vision.mlpackage # 406 MB — 448×448 → 196 tokens + 3 DeepStack taps
The vision encoder is loaded only when an image is in the prompt. DeepStack taps from vision layers 5/11/17 are injected into text layers 0/1/2 via chunk_0_vision.
model_config.json — Core ML serializes shapes into each .mlpackage. coremltools opens them without external config.from transformers import AutoTokenizer, AutoProcessor
tok = AutoTokenizer.from_pretrained("Qwen/Qwen3-VL-2B-Instruct")
proc = AutoProcessor.from_pretrained("Qwen/Qwen3-VL-2B-Instruct")
Vision preprocessing note: Qwen3-VL uses
mean=std=0.5(not the CLIP defaults), and the vision encoder expectspixel_values (3, 2, 448, 448)already pre-patchified — seeconversion/build_qwen3_vl_2b_vision.pyfor the exact transform.
import coremltools as ct, numpy as np
from huggingface_hub import snapshot_download
local = snapshot_download("mlboydaisuke/qwen3-vl-2b-coreml")
root = f"{local}/qwen3_vl_2b_decode_chunks"
decode_chunks = [ct.models.MLModel(f"{root}/chunk_{i}.mlpackage") for i in range(4)]
head = ct.models.MLModel(f"{root}/chunk_head.mlpackage")
embed = np.memmap(f"{root}/embed_weight.bin",
dtype=np.float16, mode="r",
shape=(151936, 2048))
vision = ct.models.MLModel(
f"{local}/qwen3_vl_2b_vision/vision.mlpackage")
For text-only prompts, skip the vision encoder and chain chunk_0..3 → chunk_head per step. For image prompts, run vision.predict once, swap chunk_0 with chunk_0_vision and inject the 3 DeepStack tensors during the first token of each image span.
Reference loop: conversion/qwen3_vl_2b_parity.py.
Swift runtime: Qwen3VL2BGenerator.swift. Pick Qwen3-VL 2B (recurrent, v1.4.0) in the model picker.
28-layer GQA text backbone + ViT vision tower.
Apache 2.0 (inherits from the base model).
More models in this format: Core ML Model Zoo — 46 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.
8 commits
0
stars
8
commits
3
linked in READMEs
Aug 15, 2026
updated
Package.swift:
.package(url: "https://github.com/john-rocky/CoreML-LLM", branch: "main"),
// In your target:
.product(name: "CoreMLLLM", package: "CoreML-LLM"),
Platforms: iOS 18+ / macOS 15+.
import CoreMLLLM
let llm = try await CoreMLLLM.load(repo: "mlboydaisuke/qwen3-vl-2b-coreml")
let stream = try await llm.generate(
[CoreMLLLM.Message(role: .user, content: "Hello!")],
maxTokens: 256
)
for await chunk in stream { print(chunk, terminator: "") }
import CoreGraphics
let cgImage: CGImage = ... // your CGImage
let stream = try await llm.generate(
[CoreMLLLM.Message(role: .user,
content: "What's in this image?")],
image: cgImage,
maxTokens: 256
)
for await chunk in stream { print(chunk, terminator: "") }
Core ML port of Qwen/Qwen3-VL-2B-Instruct — text + vision, INT8 chunked, runs on iPhone A18 ANE.
Heads up: for new work prefer the stateful variant at
mlboydaisuke/qwen3-vl-2b-stateful-coreml— same model, but KV cache lives inside ANE viaMLState+slice_updateso memory is 6× lower (264 MB vs 1.7 GB) and decode is 2× faster (24 vs 10 tok/s) on iPhone 17 Pro. This recurrent repo is kept for backward compatibility with the v1.4.0 runtime.
qwen3_vl_2b_decode_chunks/
├── chunk_0.mlpackage # 353 MB — text path: embed + L0-6
├── chunk_1.mlpackage # 353 MB — L7-13
├── chunk_2.mlpackage # 353 MB — L14-20
├── chunk_3.mlpackage # 353 MB — L21-27
├── chunk_head.mlpackage # 311 MB — final_norm + lm_head + argmax
├── chunk_0_vision.mlpackage # 353 MB — chunk_0 with DeepStack injection
├── prefill_chunk_{0..3}.mlpackage # 353 MB each — T=32 batched prefill bodies
├── prefill_chunk_0_vision.mlpackage # vision-aware prefill chunk_0
└── embed_weight.bin # 622 MB — raw fp16 embed (151936 × 2048)
qwen3_vl_2b_vision/
└── vision.mlpackage # 406 MB — 448×448 → 196 tokens + 3 DeepStack taps
The vision encoder is loaded only when an image is in the prompt. DeepStack taps from vision layers 5/11/17 are injected into text layers 0/1/2 via chunk_0_vision.
model_config.json — Core ML serializes shapes into each .mlpackage. coremltools opens them without external config.from transformers import AutoTokenizer, AutoProcessor
tok = AutoTokenizer.from_pretrained("Qwen/Qwen3-VL-2B-Instruct")
proc = AutoProcessor.from_pretrained("Qwen/Qwen3-VL-2B-Instruct")
Vision preprocessing note: Qwen3-VL uses
mean=std=0.5(not the CLIP defaults), and the vision encoder expectspixel_values (3, 2, 448, 448)already pre-patchified — seeconversion/build_qwen3_vl_2b_vision.pyfor the exact transform.
import coremltools as ct, numpy as np
from huggingface_hub import snapshot_download
local = snapshot_download("mlboydaisuke/qwen3-vl-2b-coreml")
root = f"{local}/qwen3_vl_2b_decode_chunks"
decode_chunks = [ct.models.MLModel(f"{root}/chunk_{i}.mlpackage") for i in range(4)]
head = ct.models.MLModel(f"{root}/chunk_head.mlpackage")
embed = np.memmap(f"{root}/embed_weight.bin",
dtype=np.float16, mode="r",
shape=(151936, 2048))
vision = ct.models.MLModel(
f"{local}/qwen3_vl_2b_vision/vision.mlpackage")
For text-only prompts, skip the vision encoder and chain chunk_0..3 → chunk_head per step. For image prompts, run vision.predict once, swap chunk_0 with chunk_0_vision and inject the 3 DeepStack tensors during the first token of each image span.
Reference loop: conversion/qwen3_vl_2b_parity.py.
Swift runtime: Qwen3VL2BGenerator.swift. Pick Qwen3-VL 2B (recurrent, v1.4.0) in the model picker.
28-layer GQA text backbone + ViT vision tower.
Apache 2.0 (inherits from the base model).
More models in this format: Core ML Model Zoo — 46 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.
8 commits