laya-apple
Correctness-validated heterogeneous Laya runtime for Apple silicon. It runs the MLX GPU and the Apple Neural Engine at the same time, and uses the Neural Engine only where it has been proven to give the same decisions as upstream Laya.
The gain comes from running both engines at once, not from raw ANE latency. This is the
v1.0 benchmark on one Apple M4 Max with macOS 26.6.2: one short and one long
request stream through one Laya(execution="workers") instance (see
GPU + ANE heterogeneous serving). Other Macs are untested, and you can
add yours. The method and raw data are in
benchmarks/v1.0.md.
Why this exists
Laya answers typed questions about a context (choice, score, noul) in one forward
pass. On a Mac there are two engines that can run it, with different strengths.
- Correct ANE execution. A fast Core ML export is not necessarily correct. On the
tested Mac, the ordinary Core ML export ran on the Neural Engine without any error and
changed up to 85 decisions against upstream PyTorch. laya-apple ships an ANE artifact
only after it passes a parity gate on the machine that uses it
(
docs/correctness.md). - Automatic routing. Short, validated single-question requests go to the ANE. Long or multi-question requests go to the MLX GPU. The router decides before a request runs and records why.
- Concurrent GPU + ANE serving. Both engines serve independent requests at the same time, so short requests stop queueing behind long ones.
Install
Apple silicon, Python 3.11–3.13:
pip install laya-apple
Optional extras:
pip install "laya-apple[ane]" # + the Neural Engine runtime (coremltools 9.0)
pip install "laya-apple[convert]" # + building ANE artifacts on this Mac (torch 2.7.0)
laya-apple artifacts build laya-typed-decisions # optional: build + parity-validate ANE artifacts here (~5 min)
Without the ane extra, or without a built artifact, everything runs on the MLX GPU. To run from
source or develop laya-apple, see CONTRIBUTING.md.
Quickstart (30 seconds)
from laya_apple import Laya
model = Laya.from_pretrained(
"convaiinnovations/laya-typed-decisions",
device="auto",
)
result = model.predict(
context="The customer was charged twice for the same invoice and is frustrated.",
questions={
"urgency": {
"type": "choice",
"instructions": "How urgent is this?",
"criteria": ["low", "medium", "high"],
}
},
)
print(result.answers["urgency"]["choice"], result.answers["urgency"]["probabilities"])
rt = result.runtime
print(rt.backend, rt.device, rt.routing_reason, f"{rt.latency_ms:.1f} ms")
On the tested machine:
high {'low': 0.1713, 'medium': 0.3358, 'high': 0.4929}
coreml ane validated_short_single_question_path 11.2 ms
- The first call downloads the pinned checkpoint. After that it works offline
(
local_files_only=True). - Without ANE artifacts, the same request runs on MLX and
routing_reasonsays why. - More:
examples/(basic.py,auto_routing.py,heterogeneous_serving.py) and the user guide.
How auto routing works
| Request | Goes to | Why (measured on the tested Mac) |
|---|---|---|
| One question, ≤ 128 tokens, validated artifact present | ANE | Faster: laya-typed-decisions L128 takes 9.9 ms on the ANE against 12.2 ms on MLX (forward P50) |
| Longer context | MLX GPU | MLX is faster there: 19.2 ms at L256 and 71.0 ms at L1024 |
| Several questions | MLX GPU | MLX batches the questions; the ANE runs them one at a time |
| Unvalidated Mac, missing artifact, or no Core ML | MLX GPU | Recorded as platform_not_validated, ane_artifact_unavailable or ane_runtime_unavailable |
The production threshold is more conservative than the measured crossover.
laya-multilingual is slightly faster on the ANE at exactly 256 tokens (8.3 ms against
8.6 ms), but that bucket stays explicit-only, because it does not beat MLX at the previous
bucket, 128 tokens. Every result carries routing_reason.
How the thresholds are derived: docs/support-matrix.md. How
the pieces fit together: docs/architecture.md.
GPU + ANE heterogeneous serving
with Laya.from_pretrained("convaiinnovations/laya-typed-decisions", execution="workers") as model:
futures = [model.submit(context=c, questions=q) for c, q in requests] # thread-safe
- The GPU runs in a worker process, and the ANE on its own dispatcher.
- Each request runs on one device, chosen by the router.
- Under load, the router also compares queue backlogs.
Short-request P99 under open-loop bursty arrivals, measured from arrival with queueing included (v1.0, same arrival sequence for both):
| Model | GPU-only | GPU + ANE |
|---|---|---|
| laya (46.2 req/s offered) | 1538.0 ms | 108.5 ms |
| laya-multilingual (83.8 req/s) | 2052.3 ms | 29.6 ms |
| laya-typed-decisions (35.8 req/s) | 1592.9 ms | 79.5 ms |
A single short request is not dramatically faster on the ANE (for example 9.9 against 12.2 ms). The gain comes from using both engines at once.
Correctness
Parity against upstream Laya on PyTorch CPU FP32, over the shipped golden rows (v1.0). Each cell gives hard mismatches, then the max probability error.
| Implementation on the tested Mac | laya | laya-multilingual | laya-typed-decisions |
|---|---|---|---|
Ordinary Core ML export · CPU_AND_NE |
❌ 12, 0.56 | ❌ 85, 1.0 | ❌ 19, 0.42 |
Ordinary Core ML export · CPU_AND_GPU |
✅ 0, 0.0066 | ✅ 0, 0.0059 | ✅ 0, 0.0028 |
| laya-apple MLX FP16 | ✅ 0, 0.0037 | ✅ 0, 0.0045 | ✅ 0, 0.0017 |
| laya-apple ANE FP16 | ✅ 0 (1 near-tie), 0.012 | ✅ 0, 0.013 | ✅ 0, 0.0077 |
- The FP16 gate: probability error ≤ 0.02 and 0 hard mismatches.
- Near-tie flips (upstream's top-two margin < 0.04) are listed, not hidden.
- Explicit ANE requests never fall back. They run the validated artifact or raise, and
every loaded artifact is also timed against
CPU_ONLYto catch a silent CPU placement. - Definitions, every configuration tested, and the fallback audit are in
docs/correctness.mdanddocs/no-silent-fallback.md.
Supported models and platforms
| Model | max_len | MLX GPU | ANE buckets (explicit) | ANE buckets used by auto |
|---|---|---|---|---|
convaiinnovations/laya |
512 | FP16 / FP32, any length | 64, 96, 128 | 64, 96, 128 |
convaiinnovations/laya-multilingual |
1024 | FP16 / FP32, any length | 64, 96, 128, 256 | 64, 96, 128 |
convaiinnovations/laya-typed-decisions |
1024 | FP16 / FP32, any length | 64, 96, 128 | 64, 96, 128 |
Tested:
- Apple M4 Max, macOS 26.6.2, MLX 0.32.2, coremltools 9.0;
- Python 3.11–3.13.
Other Apple silicon:
- MLX is expected to work.
autostays on MLX until artifacts are built and calibrated on that machine (laya-apple calibrate).
See docs/compatibility.md and the community matrix in
docs/community-benchmarks.md.
Reproduction
Each headline number above traces to a report, raw data, a command and an environment in
docs/reproducibility.md. The full v1.0 suite, which compares
PyTorch CPU/MPS, the ordinary Core ML export, MLX and laya-apple, is in
benchmarks/v1.0.md. The quick check for your own Mac:
uv run python scripts/hardware_report.py --quick
Contributing
The most useful first contribution is a benchmark from a Mac other than an M4 Max: run
the command above and open a PR with hardware-results/
(how).
CONTRIBUTING.mdcovers setup, test tiers (which tests a change actually needs), parity checks and backend changes.- Open work is labelled
good first issue,help wantedandresearch.
Limitations
- One test machine. Every benchmark is from one Apple M4 Max on macOS 26.6.2. Routing thresholds are not assumed to hold on other Apple SoCs.
- Long contexts stay on MLX, which is faster there. The ANE path is batch 1 only.
- Isolation is partial. Under concurrency, each stream's P99 is above its solo value.
- Cold start on a fresh artifact location costs 3–5 minutes of Core ML compile per
model.
ane_startup="background"serves on MLX in the meantime. choicedecisions can depend on option order. This comes from upstream Laya, and laya-apple reproduces it exactly (research/option-order/).- Not measured yet: energy use, quantized artifacts and cross-SoC validation.
More
- User guide:
docs/guide.md. - Stable API:
docs/api.md. - Architecture:
docs/architecture.md. - Changes:
CHANGELOG.md. - Security:
SECURITY.md.
Apache-2.0; see LICENSE and NOTICE. Model weights are downloaded
from their pinned Hugging Face revisions and are not redistributed. This is an independent
project, not an official release of Convai Innovations, Apple or MLX.
Release files for laya-apple 1.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| laya_apple-1.0.2.tar.gz | 148.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| laya_apple-1.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 307.7 kB
Release files / laya_apple-1.0.2.tar.gz
| Download URL | laya_apple-1.0.2.tar.gz |
|---|---|
| Size | 148.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c4910c672d7827e3d9918288311a6ecab4ff1861bff319d72d1b4338f6d02beb
|
|
BLAKE2b-256 checksum How to use checksums |
be09b390e6cfc0e33370edede397482b437bf48a9ccb2803a3d8040c618f07b2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logRelease files / laya_apple-1.0.2-py3-none-any.whl
| Download URL | laya_apple-1.0.2-py3-none-any.whl |
|---|---|
| Size | 159.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d3ef569f0ce8bdc6ec28ee1f06a6e3150ca836e9698b7796b32f3b2488fb170c
|
|
BLAKE2b-256 checksum How to use checksums |
70bc8ec964a1926ec19aef446b0e4afac253e497b4b597114af9873ca87f6cd6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency log