You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
What I'm doing: running a custom-fine-tuned FunctionGemma-270M as an on-device function-call router. Model bundled to .litertlm (int8 + fp16) with litert-lm-builder from a litert-torch .tflite. Loaded via ModelType.functionGemma, ToolChoice.required.
The problem in one line: I can't find a usable on-device configuration — the GPU backend won't run the model at all, and the CPU backend works correctly but is ~10s per call on an iPhone 13.
Results (4 experiments):
Build
Backend
Where
Result
int8 .litertlm
gpu
iPhone 13 (Metal)
Degenerate <pad> spam — wrong output, no crash
fp16 .litertlm
gpu
iPhone 13 (Metal)
OOM crash — EXC_RESOURCE high-watermark 2348 MB in dequantize::DequantizeImpl
int8 .litertlm
cpu
iOS Simulator
✅ Clean, correct routing — p50 ~960ms
int8 .litertlm
cpu
iPhone 13 (A15)
✅ Correct routing, but ~10s/call (first call ~5s, drifting up to ~10s — looks like thermal throttling)
The simulator-CPU run is the control: the container/tokenizer/metadata are correct, so the GPU failures are backend-specific and the on-device CPU latency is a hardware/runtime question, not a bad bundle.
On the ~10s/call: each call uses a fresh createChat(tools: …) and so re-prefills the full ~700-token tool prompt every time (the user query itself is only ~15 tokens), with maxTokens=2048. I suspect most of the wall-clock is re-prefilling that constant tool prefix on every call — which leads to question 4 below.
fp16 reaches the GPU compute path but weight conversion appears to materialize ~fp32 copies of the 543 MB weights → peak exceeds the iPhone-13 cap. Runner.entitlements has increased-memory-limit + extended-virtual-addressing (confirmed in the signed binary), but the crash still hit the un-entitled ~2348 MB ceiling.
Questions:
Recommended quant/format for iOS Metal GPU with a custom fine-tune? Is Q4_0 (QAT) the only Metal-safe small format, and is there any path to produce it for a custom checkpoint (vs. only the litert-community pre-quantized models)? Will weight-only int4 (wi4_afp32) avoid the op-coverage gap, or fall back to CPU?
fp16 OOM under a debugger: does com.apple.developer.kernel.increased-debugging-memory-limit need to be present and granted via the provisioning profile for the GPU path to get the raised cap? Any guidance in the plugin's iOS setup for this?
Sampler precedence on the iOS .litertlm FFI path: are temperature/topK passed to createChat() honored, or does the bundle's LlmMetadata.sampler_params take precedence? (We saw int8-CPU in-scope accuracy drop ~4 pts vs the .task baseline and suspect default TOP_P sampling overriding our greedy intent.)
CPU latency / prompt-prefix reuse: for a router whose tool prompt is constant across calls, is there a supported way to prefill the tool prompt once and reuse the KV cache (prefix caching), rather than re-prefilling ~700 tokens on every createChat()? Is ~10s/call on an A15 expected for a 270M model under this pattern, or are we missing a faster path (e.g. session reuse, smaller maxTokens)?
Plugin: flutter_gemma 0.16.4 · Device: iPhone 13 (A15), iOS 26 · Path:
.litertlmFFI (LiteRT-LM)What I'm doing: running a custom-fine-tuned FunctionGemma-270M as an on-device function-call router. Model bundled to
.litertlm(int8 + fp16) withlitert-lm-builderfrom a litert-torch.tflite. Loaded viaModelType.functionGemma,ToolChoice.required.The problem in one line: I can't find a usable on-device configuration — the GPU backend won't run the model at all, and the CPU backend works correctly but is ~10s per call on an iPhone 13.
Results (4 experiments):
.litertlmgpu<pad>spam — wrong output, no crash.litertlmgpuEXC_RESOURCEhigh-watermark 2348 MB indequantize::DequantizeImpl.litertlmcpu.litertlmcpuThe simulator-CPU run is the control: the container/tokenizer/metadata are correct, so the GPU failures are backend-specific and the on-device CPU latency is a hardware/runtime question, not a bad bundle.
On the ~10s/call: each call uses a fresh
createChat(tools: …)and so re-prefills the full ~700-token tool prompt every time (the user query itself is only ~15 tokens), withmaxTokens=2048. I suspect most of the wall-clock is re-prefilling that constant tool prefix on every call — which leads to question 4 below.My read:
dynamic_int8(CPU/XNNPACK). The Metal partitioner seems to lack kernels for some quantized ops (embedding lookup / gather), desyncing prefill/decode →<pad>. Possibly related to Custom INT4 .litertlm conversion report for Phi-4-mini and Qwen2.5-1.5B google-ai-edge/LiteRT-LM#1748.Runner.entitlementshasincreased-memory-limit+extended-virtual-addressing(confirmed in the signed binary), but the crash still hit the un-entitled ~2348 MB ceiling.Questions:
wi4_afp32) avoid the op-coverage gap, or fall back to CPU?com.apple.developer.kernel.increased-debugging-memory-limitneed to be present and granted via the provisioning profile for the GPU path to get the raised cap? Any guidance in the plugin's iOS setup for this?.litertlmFFI path: aretemperature/topKpassed tocreateChat()honored, or does the bundle'sLlmMetadata.sampler_paramstake precedence? (We saw int8-CPU in-scope accuracy drop ~4 pts vs the.taskbaseline and suspect default TOP_P sampling overriding our greedy intent.)createChat()? Is~10s/callon an A15 expected for a 270M model under this pattern, or are we missing a faster path (e.g. session reuse, smallermaxTokens)?Happy to share the bundle or a minimal repro.