Julia-1 MLX
Julia-1 for Apple silicon. Runs Supersonic Labs' Julia-1 decision model on the Mac's GPU with MLX: the same answers as the official PyTorch runtime on its published evaluations, 5–14× faster on the same Mac.
Julia-1 is a 144M-parameter decision model: given context, a question, and 2–20 candidate answers, it picks one and scores every option. It handles classification, routing, ordered scales, and yes/no decisions. This package loads the original checkpoint unchanged: there is no conversion step and no second copy of the weights.
- Parity. In FP32, the scores on typed-decisions and on the AG News and DAIR Emotion pilots match Supersonic's published CPU run exactly. Every test picks the same winner as PyTorch, with logits within 0.00115.
- Speed. On an M4 Pro, compared with the reference runtime on the same machine: 6.62 ms per call instead of 34.15 ms, 143 decisions/s instead of 24.5 in batches, and 5.91 s instead of 83.41 s for 16 requests of 8,192 tokens.
- Full interface. The typed-question API, the list API, the hierarchical
Router, the encoding checks, gradients, and export to the upstream checkpoint layout are all included.
Julia-1 MLX is an independent project. It is not affiliated with or endorsed by Supersonic Labs.
Install
Requires an Apple silicon Mac and Python 3.11 or newer.
pip install julia-mlx
Quick start
from julia_mlx import load_model
engine = load_model("SupersonicLabs/Julia-1", max_length=1024, head_length=512, strict_encoding=True)
result = engine.predict(
state="I was charged twice for the same order.",
questions={
"team": {
"type": "choice",
"instructions": "Which team should handle this request?",
"criteria": {"billing": "Billing and payment disputes", "shipping": "Shipping and delivery", "access": "Account access and login"},
},
"urgency": {"type": "score", "instructions": "How urgent is this request?", "criteria": ["Low", "Medium", "High"]},
"refund": {"type": "noul", "instructions": "Should this customer receive a refund?", "criteria": {"false": "No refund", "true": "Refund the duplicate charge"}},
},
)
print(result["answers"]["team"]["choice"], result["answers"]["urgency"]["score"], result["answers"]["refund"]["noul"])
load_model accepts a Hugging Face repo ID or a local checkpoint directory. For a repo ID it downloads only the files it needs (weights, configs, tokenizer) into the Hugging Face cache. max_length=1024, head_length=512, strict_encoding=True are the settings Supersonic evaluated. With strict_encoding=True, the engine raises an error rather than silently truncating an input.
The CLI scores one JSON request:
julia-mlx SupersonicLabs/Julia-1 request.json --max-length 1024
API
engine.predict(state=..., questions=...)is the named-question API:choicereturns the winning key,scorethe expected level, andnoulthe probability of true, each with per-option probabilities. As in the upstream API, anoulquestion takes optionalcriteriathat describefalseandtrue, and falls back to the literal words without them. Supply descriptions when you have them: on typed-decisions, the literal words score 391/600 Boolean questions instead of 483.engine.predict(rows)andengine.logits(rows)take upstream-style rows (state,question,options,type).probabilities=Falsereturns indices only, andengine.encoding_info(rows)reports how each row was encoded and whether anything was truncated.Router(engine).route(row)handleschoicequestions with up to 4,096 options. It narrows the options in groups of up to 20 and reranks the survivors, exactly as the upstream router does.
Accuracy
The release gates rerun Supersonic's published CPU evaluation (record) on the same pinned data and settings. FP32 must reproduce the published counts and pick the same winner as the committed PyTorch reference on every example.
| Evaluation | Published (PyTorch CPU) | MLX FP32 | MLX FP16 | Same winner as PyTorch (FP32 / FP16) | Max logit difference (FP32 / FP16) |
|---|---|---|---|---|---|
| typed-decisions (choice / noul / score) | 1,451 / 2,000 (426 / 483 / 542) | 1,451 (426 / 483 / 542) | 1,451 | 2,000 / 1,998 of 2,000 | 0.00115 / 0.46 |
| AG News pilot, 4 labels | 94 / 100 | 94 | 94 | 100 / 100 | 0.000195 / 0.132 (both pilots) |
| DAIR Emotion pilot, 6 labels | 86 / 100 | 86 | 86 | 100 / 100 | 0.000195 / 0.132 (both pilots) |
| Banking77 pilot, 72 labels | 60 / 100 via an unpublished shortlist | 62 via Router |
62 via Router |
100 / 100 | n/a (routed) |
The pilots rebuild the 100-example BTZSC samples of the pinned Jev benchmark protocol and check them against its manifest hash. Supersonic's Banking77 run narrowed the 72 labels with a ranking shortlist whose procedure is not published, and abstained three times. Here, Banking77 runs the package Router over all 72 labels, where PyTorch also scores 62, and is gated on agreement with PyTorch. The MASSIVE results on the model card come from a CUDA BF16 run with no published protocol, and are not reproduced.
Performance
Apple M4 Pro (64 GB), measured with benchmarks/suite.py. Each configuration runs in a fresh process with the default load_model settings, on unique typed-decisions inputs, with encoding and token caches disabled for both runtimes. The reference is the upstream PyTorch runtime on the same machine's CPU, at its default of 4 threads (10 threads measured no faster); upstream has no Apple GPU path. Peak footprint is macOS's "peak memory footprint" from /usr/bin/time -l, which includes MLX's allocator cache.
| Workload | PyTorch CPU FP32 | MLX FP32, resident embedding | MLX FP32 (default) | MLX FP16 |
|---|---|---|---|---|
| Single call, median / p95 | 34.15 / 38.97 ms | 6.65 / 6.83 ms | 6.62 / 6.80 ms | 6.41 / 6.51 ms |
| 2,000 typed questions, batch 16 | 24.46 /s | 139.73 /s | 143.37 /s | 169.02 /s |
| 16 requests × 8,192 tokens | 83.41 s | 5.87 s | 5.91 s | 4.97 s |
| Peak footprint, single calls | 699 MiB | 2,353 MiB | 1,971 MiB | 1,415 MiB |
| Peak footprint, 2,000 questions | 704 MiB | 7,027 MiB | 6,861 MiB | 5,870 MiB |
| Peak footprint, 16 × 8,192 tokens | 50,497 MiB | 8,984 MiB | 8,605 MiB | 7,360 MiB |
| Load time | 2.55 s | 0.46 s | 0.42 s | 0.41 s |
Supersonic's own measurement of the reference runtime on an Apple M4 is 33.15 ms per call (hardware record), in line with the PyTorch column.
How the speed and memory are obtained:
- Attention. Julia-1 keeps mmBERT-small's attention layout: every third layer attends globally, and the other 14 attend within ±64 tokens (
local_attention=128). Upstream evaluates those local layers densely against a fulltokens × tokensmask. Beyond 512 tokens, this runtime computes the same attention exactly in 64-token blocks against their neighbors, so local-layer cost grows linearly with length. Below that, a dense boolean mask is faster. - Embedding. The 256k-row vocabulary embedding is 98M of the 144M parameters, and a request reads only the rows for its own tokens. By default (
embedding="mapped"), the table stays a read-only memory map ofmodel.safetensors. Each forward gathers its unique rows on the CPU (about 0.1 ms) and sends only those to the GPU, so MLX holds 175 MiB of weights instead of 550 MiB. The rows are the checkpoint's own FP32 bytes, so results are identical toembedding="resident", which loads the whole table into MLX. - Precision.
dtype="float16"runs the encoder projections in half precision. The residual stream, norms, embeddings, and decision head stay FP32: from the middle layers on, Julia-1's residual stream carries outlier channels near 4,000, where FP16 cannot represent small updates. FP32 is the parity mode. - Allocator cache. MLX keeps freed buffers for reuse, and varied batch shapes would otherwise grow that cache without bound (26.0 GiB over 2,000 typed questions).
load_model(memory_cache_limit="auto")caps it at the smaller of 4 GiB and one eighth of the GPU's recommended working set; pass a byte count to override it, orNonefor MLX's default. Batches pad to multiples of 32 tokens so buffers get reused.engine.set_memory_cache_limit,engine.memory_stats, andengine.trim_memoryadjust the cache at runtime.
| Cache cap | Single call | 2,000 questions | 16 × 8k tokens | Cache held after batch run |
|---|---|---|---|---|
| 256 MiB | 8.50 ms | 134.8 /s | 6.77 s | 269 MiB |
| 1 GiB | 6.77 ms | 139.6 /s | 6.56 s | 1.0 GiB |
| 4 GiB | 6.77 ms | 143.4 /s | 6.04 s | 4.0 GiB |
| none | 6.77 ms | 145.6 /s | 6.02 s | 26.0 GiB |
PyTorch's footprint on short inputs is lower because it keeps no allocator cache. Much of MLX's batch footprint is that cache: a 1 GiB cap keeps 3 GiB less memory reserved than the default 4 GiB, for about 3% lower batch throughput and 9% slower long inputs. For a server taking single requests, two threads that each tokenize and score their own requests on their own stream (mx.stream(mx.new_stream(mx.gpu))) raised throughput from 120.2 to 163.7 requests/s, with outputs identical to serial calls, while median latency rose from 6.73 ms to 9.31 ms. Four threads reached 172.4 requests/s at a 17.86 ms median, and eight added nothing further.
Fine-tuning and export
The MLX model is differentiable, and its gradients match PyTorch's. model.train() enables the decision head's 0.1 dropout, including attention-weight dropout, exactly where PyTorch's TransformerEncoderLayer applies it. The unused temperature buffer is frozen. Load with embedding="resident" to train the vocabulary embedding. save_model(engine, directory) writes an FP32 checkpoint in the upstream layout, which julia.inference.load_model loads directly.
Verification
git clone https://github.com/zm2231/julia-mlx && cd julia-mlx
pip install -e ".[eval,test]"
hf download SupersonicLabs/Julia-1 --local-dir Julia-1
JULIA_CHECKPOINT=Julia-1 PYTHONPATH=Julia-1 pytest -q tests
python evals/typed_decisions.py SupersonicLabs/Julia-1
python evals/pilots.py SupersonicLabs/Julia-1
PYTHONPATH=Julia-1 python benchmarks/suite.py Julia-1
PYTHONPATH=Julia-1 makes the upstream PyTorch runtime importable. Without it, the PyTorch comparisons in the tests are skipped, and both gates still run against the committed PyTorch reference outputs in evals/reference/ (regenerate them with --backend torch --write-reference PATH). The gates' logit tolerance is 2e-3: FP32 MLX differs from PyTorch CPU by at most 0.00115 on the 2,000 typed-decisions questions and by at most 0.000195 on the AG News and Emotion pilots. The synthetic-input parity tests keep a 2e-4 bound.
Attribution
- Julia-1, including its weights, decision head, marker serialization, typed API, and router, is by Supersonic Labs, released under Apache-2.0 at SupersonicLabs/Julia-1. This repository contains no Julia-1 weights;
load_modelfetches them from that repository. - mmBERT-small (jhu-clsp/mmBERT-small, Johns Hopkins CLSP) is the multilingual encoder and tokenizer that Julia-1 builds on.
- ModernBERT (Answer.AI and LightOn) is the encoder architecture.
- The MLX ModernBERT encoder derives from the MIT-licensed pappitti/modernbert-mlx, which in turn builds on Apple's MIT-licensed mlx-examples.
- Evaluation data: LocalLLaMA/typed-decisions, BTZSC, and the Jev benchmark protocol by AbdelStark.
License
The code in this repository is MIT-licensed (see LICENCE). The Julia-1 weights are Apache-2.0 and remain in the upstream model repository under their own license.
Metadata
Release files for julia-mlx 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| julia_mlx-0.2.0.tar.gz | 33.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| julia_mlx-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 58.2 kB
Release files / julia_mlx-0.2.0.tar.gz
| Download URL | julia_mlx-0.2.0.tar.gz |
|---|---|
| Size | 33.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4cf498baa2213cf94743b7df32a5f8a871a048838cee2b3903a034a0a9fa9037
|
|
BLAKE2b-256 checksum How to use checksums |
94ea5974a144acbe64662fb942e0a58b9f9c8b3d865725498fc0b7672a02914c
|
| 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 27, 2026.
Transparency logRelease files / julia_mlx-0.2.0-py3-none-any.whl
| Download URL | julia_mlx-0.2.0-py3-none-any.whl |
|---|---|
| Size | 25.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ef8961c24592bd3788f204dbfecfe934af1fbe9429ff204831bf88986507ba32
|
|
BLAKE2b-256 checksum How to use checksums |
a2bec46a76d06f4a831e55846508f16f8a2433ec95b9c13fab4f15d3a61d24ae
|
| 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 27, 2026.
Transparency log