Phi-4-mini-reasoning — LiteRT-LM (blockwise int4)
microsoft/Phi-4-mini-reasoning converted to
the LiteRT-LM (.litertlm) format for on-device inference with Google's
LiteRT-LM runtime (the engine behind the official
litert-community/* models).
Phi-4-mini-reasoning is a dense 3.8B math/reasoning model from Microsoft (implemented as
Phi3ForCausalLM, 32 layers) — it solves problems with a <think>…</think> chain-of-thought, then
the answer.
⚠️ It's a reasoning model — give it room to think
This model emits a <think>…</think> chain-of-thought, then a \boxed{} answer. Run it with
max_tokens ≥ 2048 — at a short limit it gets cut off before the answer. (All quality numbers below
were measured at 2048.)
Performance
litert-lm benchmark (litert-lm 0.15.0) on an Apple M4 Max, -p 256 -d 256 --runs 3 (the tool averages three iterations), max-num-tokens 4096, warm-up run discarded, otherwise idle machine.
Reproducibility: the GPU rows repeat to within about 1% across invocations; the CPU rows are noisier — re-running the 1B control six times spread its CPU decode over 29.0–33.3 tok/s, so treat the CPU column as accurate to roughly ±7%.
The model loads and generates correct answers on an iPhone 17 Pro, but no on-device timing was taken, so none is quoted.
Accuracy note
Measured on GSM8K (n=100, greedy, 0-shot chain-of-thought, max_tokens 2048, identical prompt and
answer-extraction for every row).
int4 (block 32) is at parity (−8 pt). Why block 32 (not block 128)? This is a precision-sensitive
math model: the coarser block-128 int4 dropped to 74% (−15 pt) and degenerated on some prompts, while
block 32 holds at 81%. So only the block-32 build is published.
Galaxy S26 — GPU backend
The published bundle runs on the Android GPU backend and generates.
Measured on a Samsung Galaxy S26 (SM-S942Q / SM8850, Android 16) with litert_lm_advanced_main from litert-lm 0.16.0, --backend=gpu --sampler_backend=cpu, prompt What is the capital of France?. Peak is the process high-water mark (VmHWM) sampled during that same run. Gated 2026-08-24.
The op counts above are the LiteRT GPU partitions. XNNPACK additionally takes 1 of the 4 nodes in decode_embedder and 1 of the 4 nodes in prefill_embedder_128; the runtime accepts that split.
No speed rows, on purpose. On this handset the GPU backend wins prefill and does not win decode, so a GPU throughput figure only means something beside a CPU row from the same handset, and no S26 CPU row exists for this model yet.
GPU wiring, including the Gallery import toggle: GPU guide.
Usage
The .litertlm bundle carries the tokenizer and prompt template (Phi format — <|user|>…<|end|><|assistant|>),
so no separate tokenizer files are needed.
Run on Android
Update (July 2026): Google AI Edge Gallery v1.0.16+ can import litert-lm models directly from Hugging Face inside the app (tap +) — no computer or adb needed. The manual steps below are only required on older builds or for sideloading a local file.
The official Google AI Edge Gallery app runs
.litertlm models on-device:
- Install a recent Gallery (package
com.google.ai.edge.gallery, 1.0.15+ supports .litertlm).
- Download
model.litertlm and push it: adb push model.litertlm /sdcard/Download/
- In the app tap +, pick the file, choose the GPU backend, and raise the max-tokens setting (≥2048).
Run on desktop (LiteRT-LM CLI)
The same .litertlm bundle runs on macOS / Linux / Windows with the official
LiteRT-LM CLI — including as a
local OpenAI-compatible API server:
Run on iPhone
Verified on iPhone 17 Pro (LiteRT-LM Swift runtime): loads and generates correct answers. This is a
~2.6 GB bundle (Phi's 200K-token vocab makes a large externalized embedder), so it sits near the iOS
memory ceiling — if you hit "embedding lookup model is not initialized" (a low-memory symptom), reboot
the phone to free RAM and reload.
Conversion
Converted with the official litert-torch
converter. Phi-4-mini uses the Phi3ForCausalLM arch with LongRoPE + a (nominal) sliding window;
two export-time adjustments are needed for current litert-torch:
- LongRoPE: replace
Phi3RotaryEmbedding.forward with a static version (the @dynamic_rope_update
seq-len branch is data-dependent under torch.export; for cache ≤ original_max=4096 the short factor
is always correct).
- Sliding window: set
config.sliding_window=None (it is 262144 ≫ context, i.e. full-causal) so
the standard causal mask path is used.
Recipe: blockwise-32 int4 + OCTAV, embeddings INT8, KV cache 4096, externalize_embedder=True.
2026-08-28 — start_token fix (weights unchanged)
The LiteRT-LM engine prepends the metadata start_token to every prompt, but this model's reference prompt has no leading BOS at all — the bundle was feeding an extra <|endoftext|> the model was never trained on. The start token has been removed.
Metadata-only change: every section of the bundle except the LlmMetadata block is byte-identical to the previous file (verified by sha256 per section), so the weights, the graph and the tokenizer are unchanged and the speed and memory numbers on this card still describe exactly this file — only the file's own sha256 differs. What changed is the input: the token stream the model reads for a given conversation can differ from the previous file's, and it now matches this model's own reference chat stream. Greedy decoding can turn on a single token, so an individual answer can differ from the previous file in either direction. Unless a row says otherwise, the accuracy figures on this card were measured on the previous file and have not been re-measured on this one. If you downloaded before 2026-08-28, re-download.
2026-08-29 — default system prompt restored (weights unchanged)
The upstream chat template emits a default system turn whenever the caller sends no system message — for this model: Your name is Phi, an AI math expert developed by Microsoft.. The converter's template probe renders the template with a system message already present, so that block was never seen and never reached the bundle: with no system message the model was running without the default system turn it was tuned with. The chat template in model.litertlm now emits the block exactly once when no system message is given. In model.litertlm, the block is not emitted when you pass a system message; the upstream template also wraps a caller's system message in its own preamble, and this file passes it through unchanged, exactly as it did before. The restored block adds 15 prefill tokens to a conversation that sends no system message, so time-to-first-token grows by that much; per-token speed is unchanged.
Metadata-only change: every section of the bundle except the LlmMetadata block is byte-identical to the previous file (verified by sha256 per section), so the weights, the graph and the tokenizer are unchanged and the speed and memory numbers on this card still describe exactly this file per token — only the file's own sha256 differs. What changed is the input: with no system message, the prompt now renders byte-identical to the upstream chat template's output, verified on the LiteRT-LM runtime. A system message you pass yourself renders as before. Greedy decoding can turn on a single token, so an individual answer can differ from the previous file. Unless a row says otherwise, the accuracy figures on this card were measured on the previous file and have not been re-measured on this one. If you downloaded before 2026-08-29, re-download.
2026-08-31 — thought channel declared (metadata only, weights unchanged)
model.litertlm now declares the reasoning channel in its metadata (LlmMetadata.channels: channel name thought, markers <think>…</think> exactly as this model emits them). Without the declaration the runtime has no way to tell the reasoning apart from the answer: the raw thinking streamed inline into the visible text, and a thinking_token_budget was silently ignored (the API returns OK and only logs a warning). With the channel declared, LiteRT-LM returns the reasoning separated in channels["thought"] and the thinking budget takes effect.
Metadata-only change: every section of the bundle except LlmMetadata is byte-identical to the previous file (verified per section, tokenizer included), so the weights, the graph, the tokenizer and the chat template are unchanged and the speed and accuracy numbers on this card still describe this file — only the file's own sha256 differs. Verified on the LiteRT-LM runtime (litert-lm-api 0.16.1): the visible answer stays clean, the reasoning lands in channels["thought"], and on one bundle of this batch thinking_token_budget=16 was confirmed to truncate the reasoning at exactly 16 tokens where it was a no-op before. If you downloaded before 2026-08-31, re-download to get the channel-aware file.
License
MIT, inherited from the base model
microsoft/Phi-4-mini-reasoning.