Core AI model zoo

Static-shape stateful decode + stock-runtime VLM (Unlimited-OCR)

Lessons from porting baidu/Unlimited-OCR (DeepseekV2 R-SWA MoE doc-OCR) to Core AI on the stock coreai.runtime — no engine patch, no static-input hook. Transferable to any sliding-window / bounded-cache decoder and any inputs_embeds-driven VLM.

1. A growing decode shape faults on Metal 4 — make the graph fully static

The classic flat-latency goal: R-SWA / sliding-window attention keeps the attended set constant, so decode latency should be flat. The trap is that the obvious implementations all keep a dynamic shape that the runtime re-specializes per step:

Any of these recompiles the Metal shader as seq_len grows. On older stacks this is the Qwen3-Coder-Next freeze (periodic stalls). On Metal 4 / macOS 26 it is worse — the runtime faults (Failed to import MPS module + MTL4CommandQueueErrorDomain error 1, command-buffer dies) on the 2nd distinct shape: the first shape compiles and runs, the second recompile crashes.

Fix — a fully-static decode graph. Inputs are inputs_embeds [1,1,H] + pos [1] (int32, the absolute position as a runtime value, not a shape). Then:

Result: no tensor shape ever changes → the engine compiles once → flat ~12.7 ms/token (max/median 1.22×). Cost: the SDPA attends the full buf_len (e.g. 2048) every step instead of Lm+W (~243); negligible vs the MoE FFN, and worth it for stock-runtime stability.

What did NOT work

2. SDPA can’t be externalized when you need a runtime mask

The engine-native (externalized) SDPA op takes scale / is_causal / window_size as attributes — it does not accept an arbitrary runtime mask tensor. R-SWA (global prefix + sliding window) isn’t expressible as is_causal+window_size, so you must feed a custom bool mask → don’t externalize SDPA (it lowers as plain matmul/softmax/mask). Keep RMSNorm externalized. Passing a runtime mask to an externalized SDPA produces a malformed graph that command-buffer-faults.

3. Driving a stateful, inputs_embeds VLM on the stock runtime (no patch)

The zoo’s other VLMs ride the pipelined engine + apps/coreai-pipelined-static-inputs.patch (image embeds in a bound MTLBuffer, extension ids V+slot, engine.generate(tokens)). That needs the patch and a token-driven graph. The pure-export alternative, which works on stock coreai.runtime:

4. Quantization consistency + greedy repetition

5. The visual-token arrangement is host-side and exact

Base mode (image_size 640, crop_mode=false): vision .aimodel → 100 patches → view(10,10) → append image_newline after each row (→110) → append view_seperator (→111) → masked_scatter into embed_tokens(input_ids) at the <image> (id 128815) positions, where input_ids = [BOS, <image>×111, "document parsing."] (Lm = 115). Reconstructs the reference prefix exactly (cos 1.000000, |Δ| = 0). Ship embed_tokens + image_newline + view_seperator + prompt_input_ids as raw tensors so the host (Swift) does the arrangement without the full model.

See models/unlimited-ocr/README.md, conversion/unlimited_ocr/, apps/CoreAIOCR.