All notable changes to CoreAIKit are documented here. The project follows
Semantic Versioning — pre-1.0, minor versions may change API,
patch versions never do. See docs/STABILITY.md for the full
policy.
iOS dynamic-KV guard — the engine (coreai-models 0.2.1-zoo) now caps a pipelined
turn’s KV pre-grow at capacity 1024 on iOS for dynamically-sized-KV bundles: the on-device
compiler miscompiles those specializations at seq ≥2048 (corrupt output from token 1).
ChatDemo additionally clamps maxResponseTokens to 896 on iOS as belt-and-suspenders.
Tracked in #5; upstream
apple/coreai-models#124. Both guards come out when the compiler fix lands.
Nanbeige4.2 3B (nanbeige4.2-3b) — chat, the catalog’s first community-contributed
model: ported and published by @ukint-vs
(zoo PR #6), served from the
contributor’s HF repo at a pinned revision. A recurrent Llama — 22 physical blocks execute
twice per token (44 KV-cache layers) — int8, 4.6 GB, thinking chat template. Device-gated
on iPhone 17 Pro: token-exact vs the fp32 oracle (24/24 ×2 runs), 8.5 prefill / 6.4 decode
tok/s settled (two-pass models pay ~2× weight traffic per token; Mac M4 Max: 57 tok/s).
KitSeparator — music source separation (Mel-Band RoFormer, Kim Vocal): a song in, a
vocals stem and an instrumental stem out. separate(_:) on a decoded stereo mix or
separate(contentsOf:) on any file AVFoundation reads. New catalog kind separation
(melband-roformer-vocal). iPhone 17 Pro: an 8 s chunk in 1.23 s (~6.5x real-time).KitDialogue — multi-speaker / dialogue text-to-speech (VibeVoice-Realtime-0.5B), the first
kit API that performs a script: perform("Speaker 1: …\nSpeaker 2: …") renders each turn from
its own voice preset and concatenates them. 25 packaged voices, free text (Qwen2.5 tokenizer +
mmapped fp16 embedding table — no torch, no 272 MB read). KitSpeaker(catalog:) also accepts
the id and speaks one line in the default voice. Pairs with KitDiarizer for a
generate → diarize loop.AudioFile.pcmStereo(_:sampleRate:) — decode any audio file to stereo at a chosen rate (the
separation models need the full band, not the 16 kHz speech downmix).StatefulGraphModel.seedState(keys:values:prefillLength:) — seed a decode bundle’s KV cache from
an external prefill (a voice prompt, a cached prefix) instead of replaying it.
SECURITY.md — the trust boundary written down: two hosts (GitHub raw for
the catalog, Hugging Face for the weights), revision pins as the integrity mechanism, and the
parts that are absent — catalog.json is unsigned, downloads carry no independent checksum
manifest, bundles are not code-signed. Includes the escape hatch for production apps:
ModelCatalog.load(from:) takes a URL, so you can host the catalog yourself.
AGENTS.md — the contract for coding agents building on the kit: which layer
to reach for (Apple’s Foundation Models first), the catalog as data to read rather than guess
at, the failures that only appear on a real device, and an accurate statement of what
“verified” covers.ModelCatalog.load() falls back
to the compiled-in ModelCatalog.builtin when the network fetch of catalog.json fails, and
that literal carried no revision pins — while CatalogEntry.modelID(path:) resolves
revision ?? "main". A network failure therefore downgraded a shipped app from the exact bytes
that were gated to whatever the model repository’s main branch held at that moment, which is
the opposite of what the catalog documents. Pins are now generated from catalog.json into
BuiltinPins.swift and overlaid onto the literal; CI fails if the two drift, and
BuiltinPinsTests fails if any entry reaches builtin without a pin. Apps pinned to 0.2.0
or earlier are affected — the fallback path is the one that matters here, so an app that has
never seen a catalog fetch failure has never hit it.catalog.json entry now carries a revision
(a Hugging Face commit hash), and every (catalog:) initializer downloads from
exactly that revision — including multi-bundle models (VL towers, TTS glue, OCR
assets, Gemma PLE tables, ASR encoder pairs). A push to a model repo can no longer
change what apps receive; pins are bumped deliberately by re-running
scripts/pin-catalog.py and committing the catalog change.CatalogEntry.revision, CatalogEntry.modelID(path:), and pinned(_:) on
ModelID, GemmaModelID, and VLModelID. Older catalogs without pins keep
resolving main; older package versions ignore the new field — the change is
backward and forward compatible.scripts/pin-catalog.py — re-pins all entries to each repo’s current head
(--check verifies every entry is pinned and reports drift, for CI).os_proc_available_memory() is now
compiled only on iOS — the newer SDK marks it unavailable on macOS, which broke
swift build for Mac consumers. Semantics are unchanged (macOS always owned
tables).catalog.json / built-in snapshot drift: nemotron-3.5-asr-streaming-0.6b was
missing from the built-in snapshot, while mineru2.5-pro, glm-ocr, and TimesFM’s
static-shape engine hint were missing from the shipped catalog.json. The sync
test now pinpoints per-entry drift and requires every shipped entry to carry a pin.KitASRModel, KitWhisperModel, KitParakeetModel, and KitForecaster
(catalog:) initializers now consult the live catalog like every other surface, so
unknown ids and wrong-platform requests fail with the same clear, early errors.<repo>/<revision>/<variant>) instead of main.Initial tagged release: ModelStore + ModelCatalog, ChatSession (streaming,
stats, guided generation), FoundationModels providers (text / VL / Gemma), vision
pipelines (CLIP, depth, detection, super-resolution, video), embeddings + retrieval,
ASR / TTS / diarization / OCR / audio-QA / diffusion-LM / forecasting surfaces, and
SwiftUI components.