Part 14 — Bridges between stacks¶
Version floor: macOS 27 / iOS 27 for anything that executes, Python 3.11+ to convert, and
a set of wheel pins that do not agree with each other. mlx2coreai 0.1.1 pins
coreai-core==1.0.0b1 exactly; 1amageek/swift-lm 0.11.0-alpha.1 pins coreai-core==1.0.0b2,
coreai-torch==0.4.1, coreai-opt==0.2.1, torch==2.9.0; and john-rocky/coreai-model-zoo's
acceptance bar requires coreai-core ≥ 1.0.0b2, because bundles exported with earlier wheels are
"rejected by the Xcode 27 beta 3+ SDK loader" (Failed to convert to versioned IR, FB23666783).
The main subject of this part pins the exact wheel the community's own bar rejects. Both Swift packages
declare platforms: [.macOS("27.0"), .iOS("27.0")] and Swift tools 6.4.
Who this is for: you already have a model working in one stack — MLX, a Hugging Face checkpoint, a
bundle someone else built — and you want it in Core AI. Also for the person writing a third bridge,
and for the Swift developer who has to consume somebody else's .aimodel bundle. This is the
Advanced part of the series: it assumes Parts 7, 8 and 12 as vocabulary and does not re-teach them.
⚠️ Read this before you trust anything in this part¶
Nothing in this part was executed. coreai-core is not installed in the environment these notes
were taken in and there is no macOS 27 SDK on the machine to compile the Swift runners against. Every
signature, flag and error string was read from source — three third-party repositories plus the
Apple repos they target — and the source is honest about being a beta-era moving target.
And the caveat the whole part turns on. mlx2coreai's own op-coverage report opens with:
"Coverage type: CoreAI asset generation. This does not imply runtime numerical parity."
✅ VERIFIED, docs/op_coverage.md:3, verbatim. Its "Asset validation: passed" line is a file-existence
check — assert (asset_path / "main.mlirb").exists(). Not an execution, not a comparison. The 156-op
table tells you what will convert; it tells you nothing about what will be correct.
🔴 GAP — nothing in our corpus verifies a converted MLX model end to end on a device. Not one token. Not one logit vector. The bridge's own CI cannot do it (there is no Core AI runtime to execute against), the packaged Python API literally cannot run the stateful asset it produces (§8.1), and no community report of a device-run
mlx2coreaimodel exists in the material behind this series. §6.3 is a parity-testing recipe you are expected to run yourself, and until you have run it you have a bundle, not a port.
Why this part exists¶
Every route into Core AI ends at the same artifact — a .aimodel, which is a directory holding
serialized Core AI MLIR, a hash and a metadata blob. What changes is the producer, and in 2026 there are
suddenly third-party producers. That creates four problems the first-party parts do not have.
- The bridge stands on private API.
mlx2coreaicallsAIProgram._from_mlir_module— a leading underscore — for its entire existence, plusCoreAITensorSpec._to_mlir_type()and aGraphOp(...)whose declaration appears nowhere in our corpus. A wheel bump can break the converter without breaking anything Apple documents. - A format became a standard without anyone publishing it.
apple/coreai-models's bundlemetadata.jsonschema"0.2"is now a de-facto interchange format: three independent producers emit byte-comparable manifests, because Apple's Swift loader is strict and because once you emit it,CoreAILanguageModel(resourcesAt:)just works on your output. If you build a bridge, this is the thing to target — and it is specified in no document. - Converting succeeds far more often than it is correct. §7 lists eight places where the
MLX→MLIR reconstruction is known to be lossy. Every one of them produces an asset that saves.
Several produce output that is plausible. That combination is what the zoo's
AGENTS.mdcalls the most expensive failure mode, "because it looks like success." - The stacks disagree with each other in public.
expectFrequentReshapeshas four sources and three verdicts — Apple's own code sets ittruefor exactly the model shapemlx2coreaiproduces,mlx2coreai's own Swift runner sets itfalseon that same shape with no comment,swift-lmthrows if you pass it, and the community has a device-validated SIGSEGV. A bridge author still has to pick a value.
Read this first: the triage table¶
There is one guide in this part, so the column that matters is the section.
| If your situation is… | Read | Why |
|---|---|---|
"I have an mlx-lm causal LM and want a Core AI bundle" |
14.1 §3, then §6 | One command; it reproduces Apple's keyCache/valueCache contract exactly. §6 is not optional |
| "I have an MLX vision or audio model — one graph, no state" | 14.1 §5 | The generic converter. ConversionConfig field by field, and the three fields that need a warning label |
| "It converted. The text is fine at step 1 and nonsense by step 30" | 14.1 §3.5, §7 | The classic KV-offset bug, and the eight lossy lowerings that produce plausible garbage |
| "I am writing my own bridge / my bundle won't load" | 14.1 §4 | Schema 0.2 field by field, what Apple's reader enforces, and a targeting checklist |
unsupported metadata_version '0.1' |
14.1 §4.2 | Absent key defaults to "0.1"; or you pointed at the .aimodel, not the bundle dir |
Failed to convert to versioned IR |
14.1 §2.3 | The wheel-pin collision, plus a ninety-second probe that settles it before a 4 GB conversion |
| "My model has sliding-window attention, an SSM block, or MoE" | 14.1 §2.4, §12.1 | make_mask raises NotImplementedError; no SSM lowering. Not this bridge |
| "I need to consume a Core AI bundle — or a VLM — from Swift" | 14.1 §9 | The densest inventory of CoreAILanguageModels outside Apple's own repo, and the three-asset VLM contract |
"Should I set expectFrequentReshapes?" · "SIGSEGV at AIModel(contentsOf:options:)" |
14.1 §10 | Four sources, three verdicts, one device-validated crash, and a safe default ladder |
| "I need to benchmark this" | 14.1 §8.3, §11.4 | The backend auto-selection trap, and why thermals move numbers 2.3–4.1× |
| "Should I convert at all?" · "Is Core AI even the right target?" | 14.1 §12 | Five cases where re-authoring is the honest answer, and a Core AI vs MLX head-to-head |
The guide in this part¶
14.1 — Bridges into Core AI: mlx2coreai, swift-lm, and the community zoo¶
Three bridges, one destination. §2–§8 cover lucasnewman/mlx2coreai — the only tool in existence
that goes MLX → Core AI without passing through PyTorch, and the vehicle for two facts worth more than
the tool itself: Core AI's IR is MLIR (the imports are literally MLIR's Python bindings, and
str(program) gives you readable MLIR text), and the bundle layout is an interchange format you can
target. §9–§10 cover 1amageek/swift-lm, one of very few real third-party Core AI integrations you
can read, including Apple's three-asset VLM contract and a documented rejection of
expectFrequentReshapes that contradicts Apple's own shipping code. §11 covers
john-rocky/coreai-model-zoo — single-author community material, attributed as such throughout, whose
porting playbook is the best written-down process for this work anywhere. §12 is the decision
table, including the case where the answer is "re-author from the checkpoint instead."
⚠️ SILENT FAILURE —
position_idsis the full position vector (§3.5). The KV write offset is computed inside the traced graph asmax(position_ids) - len(input_ids) + 1. Pass only the new positions, right-pad with a sentinel, or feed them non-monotonically, and keys and values land at the wrong slice of a 5-D cache. The arithmetic is valid for any input, so nothing throws; logits stay finite, the sampler keeps producing tokens, and the text degrades gradually. Code review will not find this — only a token-for-token comparison against the MLX original will.⚠️ SILENT FAILURE — the lowerings that convert and lie (§7). A boolean attention mask is
castand added, soTruebecomes+1.0on every visible position and masked positions are left completely unmasked — andpasses.pycomputes amask_modeattribute that would fix it which the lowering never reads. Generalconv_transposelowers to a zero constant — only the 1×1 stride-1 case gets a real implementation, and the coverage report counts the placeholder as covered (grep the MLIR forunsupported_coreai_beta_asset_writer).mx.log2/mx.log10collapse to natural log andleft_shift/right_shiftcollapse to bitwise AND, because MLX remaps them to shared primitive names and the base/op selector is dropped.allow_unknown_sources=True— the default — inventsTensorSpec(shape=(), dtype="fp32")for any tensor it cannot spec.⚠️ SILENT FAILURE — the tooling around it (§8, §9.8, §11.3–§11.4).
mlx2coreai.run_aimodel, the packaged public API, has nostate=argument, so it cannot execute the stateful asset the tool's headline command produces. The benchmark's--runtime-backend autosilently swaps Swift for Python, and the README's own invocation includes--decode, which is on the Swift backend's reject list — so the documented command quietly measures the slower path. Every Core AI test inswift-lmis gated on an env var withelse { return }, so a green run passes without running anything. Core AI conversion is not byte-deterministic (community-measured: same recipe, same machine, minutes apart —main.mlirbdiffers by 7 bytes,main.hashentirely), so a stored hash is worthless as a reproducibility criterion. And in a persistent Python runner, letting theAIModelget garbage collected while holding only the loaded function returns garbage output with no crash.🔴 GAP — the Python surface is inferred from call sites, and it is thin.
GraphOp(...)'s keyword arguments,coreai.slice_'s argument order,NDArray(data=, backing=)andfunction(inputs=, state=)appear only as calls in third-party code; no declaration exists anywhere in our corpus. Whetherfunction(inputs=, state=)mutates state in place is unstated — assume it does and never reuse a state dict across sequences.SpecializationOptions.expectFrequentReshapeshas an abstract of one sentence, no Discussion and no documented default. And nobody has read how Apple's official VLM exporter is invoked, thoughswift-lmconsumes its output. The guide's §14.3 collects fifteen open gaps, each with what would resolve it.
Reading order¶
Everyone starts with the guide's intro block and §6, then §2.3. That is fifteen minutes and it changes what you do next: §6 tells you that 156/156 op coverage is an asset-generation claim, and §2.3 tells you the wheel you are about to install may write bundles the current SDK loader rejects. Reading §3 before §6 is how people lose a week.
Then branch by what you have. An mlx-lm LLM: §3 in full (the signature, the position_ids
contract, the cache shim) → §6.3's parity recipe → §7 to interpret the failures → §8 when you need
to actually run or measure it. A non-LLM MLX model: §5 → §6 → §7, and skip §3 entirely. Writing a
bridge or a bundle producer: §4 end to end, then §9.5, which is the validator you should write on the
consuming side. A Swift developer consuming somebody's bundle: §9, plus §10 only if you touch
SpecializationOptions.
Read one thing out of order: §12.3 — is Core AI even the right destination — belongs before you start, not after. Community head-to-heads put dense transformers at tie-to-+12 % for Core AI, MoE at 0.5–0.78× (MLX wins), and exotic attention as a loss; and guided generation needs logits the GPU-pipelined Core AI path does not expose.
Deferrable. §11 is process and community catalogue — genuinely excellent, and skippable on a first
pass except §11.3 (the four-state PASS/DIFF/FAIL/skipped verdict model, and non-determinism)
and §11.4 (thermals, cpu_only() being ~9–10× slower and a parity option not a performance one).
§5.4's MLX callback event contract is only for people writing their own capture. §9.6–§9.7 are VLM-only.
§13 is a quick reference to come back to; §13.5 lists all fourteen silent failures in one table.
What this part deliberately does not cover¶
- The PyTorch → Core AI path itself.
coreai_torch.TorchConverter,get_decomp_table(),state_names,remove_functionalizationand the export pipeline are Part 8 and Part 10. This part treats them as the destination of a bridge, not the subject. - Running the result.
AIModel,InferenceFunction,NDArray,MutableViews, states, pipelined decode, bundles, engines and guided decoding: Part 7. Specialization, the model cache, AOT andexpectFrequentReshapesin its own right are Part 7 guide 02 — §10 here covers only the third-party disagreement about that flag. - MLX itself.
mx.export_function, the primitivestate()tuples, mlx-lm's cache protocol: Part 12 and Part 13. This part assumes you can already produce a working MLX model. - Compression. Neither bridge quantizes anything —
mlx2coreaihas no palettization, no int4/int8 packing and nocoreai-optintegration at all. Part 9. - Choosing a backend behind
LanguageModelSession.MLXLanguageModelvsCoreAILanguageModelvs the rest of the five conformers: Part 1 and Part 4. - Measuring whether the port is any good as a product, versus numerically faithful:
Part 6. Delivery, first-run UX and OS-update re-specialization:
Part 15. Coming from
coreai-torch0.4.x or Core ML: Part 17.
Sources for this part¶
Local clones of three third-party repositories, read on disk and never executed:
lucasnewman/mlx2coreai at HEAD 059c9f3 (11 commits, all June 2026, MIT, one author — all line
numbers in the guide refer to that commit, and the README's own history is quoted via git show to
retire a stale bf16 claim); 1amageek/swift-lm at db7a802 "Add Core AI vision language model
adapter" (2026-07-18), including its PHILOSOPHY.md, design docs and release notes; and
john-rocky/coreai-model-zoo, whose catalogue inventory is dated 2026-07-25 and whose
expectFrequentReshapes crash report is device-validated 2026-07-23 on iPhone 17 Pro —
community-sourced, single-author, benchmarks self-declared as uncontrolled, and labelled as such at
every point of use. Apple repositories for the destination contracts: apple/coreai-models
(export/bundle.py, export/_constants.py, primitives/macos/cache.py, ModelBundle.swift,
BundleKind.swift, FunctionMap.swift, LanguageConfig.swift, Runtime/ModelStructure.swift, and the
model-authoring skill's PSNR gate bars) and apple/coreai-torch. ml-explore/mlx C++ source
(export.cpp, primitives.h) for the callback event contract and the primitive name_remap collapses,
cross-verified against MLX's own test. ⚠️ No WWDC transcript is cited anywhere in this part —
nothing in the 2026 session corpus covers these bridges — and Core AI ships zero Apple sample-code
projects, so the evidence class this series normally leans on hardest is simply absent here. That
absence is the reason the parity section exists.