Views
No views yet
[ss.ss][Sxx]text[ss.ss]…).litert-torch 0.9.1 (the renamed ai-edge-torch generative API). Conversion
scripts, host runner and parity harness:
https://github.com/vieenrose/LiteRT/tree/moss-td-port/litert/samples/asr/moss_td| file | role | signatures |
|---|---|---|
moss_td_encoder_* | Whisper-medium encoder + 4x time merge + VQAdaptor | mel (1,80,3000) → audio embeds (1,375,1024) |
moss_td_embedder_* | tied token embedding / lm_head | embed_1, embed_128, logits |
moss_td_decoder_*_ekv{N} | Qwen3-0.6B blocks + final norm | prefill_128, prefill_1024, decode; inputs_embeds + input_pos + mask + external KV cache (BTNH, 28×2 × (1,N,8,128)) |
moss_td_encoder_q8.tflite + moss_td_embedder_q8.tflite +
moss_td_decoder_v2_q4b32_ekv2560.tflite + tokenizer/.moss_td_encoder_f32,
moss_td_embedder_f32, moss_td_decoder_f32_ekv6144) and a higher-fidelity
moss_td_decoder_q8_ekv2048 (dynamic-range int8, sized for 90 s windows —
no silence QAT, see below). Other variants (base int4, other ekv sizes)
were removed 2026-07-23; they remain in this repo's git history and can be
regenerated with the port's export.py.moss_td_encoder_fp16 + moss_td_embedder_fp16 +
moss_td_decoder_fp16_ekv2048, ~1.8 GB total, restored 2026-07-24
byte-identical from the pre-cleanup revision): on the GPU delegate fp16
executes natively, making this the right precision for phone-GPU inference.
Do not use it on the CPU/XNNPACK path — there fp16 is upconverted to f32
at load and OOMs 8 GB devices; use q8/int4 on CPU. Note the fp16 decoder is
the pre-QAT base model (no silence-robust training) at ekv2048 (90 s
windows).moss_td_decoder_v2_q4b32_ekv2560.tflite is exported from the project's
q4mix-v2 QAT checkpoint (moss_q4mix_v4_qat/step_50) instead of the base
weights. It fixes the base int4 decoder's silent-audio failure: on a silent
tail the base int4-b32 decoder emitted a looping English hallucination
("the audio contains a sound effect…", 1185 chars) while v2 emits nothing
(immediate EOS — the correct no-speech result). Measured deltas vs the
now-removed base int4-b32 ekv2560 decoder on the same q8 encoder/embedder
(host, compiled engine, 2026-07-23):| gate | base int4-b32 | v2 int4-b32 |
|---|---|---|
| jfk text | 100% (golden) | 100% — identical text, 2 end-timestamp digits shift |
| silent tail | 1185-char hallucination loop | empty (immediate EOS) |
| zh90s text-only vs pinned f32 ref | 98.728% | 98.094% (−0.63 pt, within tolerance) |
moss_td_encoder_q8.tflite / moss_td_embedder_q8.tflite are used unchanged.CompiledModel.from_file(path) without options lets the runtime pick the
thread count — and it picks very low (measured 8.5× slower in one production
host case). Always pass:1from ai_edge_litert.compiled_model import CompiledModel
2from ai_edge_litert.cpu_options import CpuOptions
3from ai_edge_litert.options import Options
4from ai_edge_litert.hardware_accelerator import HardwareAccelerator
5
6opts = Options(hardware_accelerators=HardwareAccelerator.CPU, # single enum, despite the plural name
7 cpu_options=CpuOptions(num_threads=N))
8cm = CompiledModel.from_file(path, options=opts) # `options` XOR `hardware_accel` kwarg — not bothInterpreter(num_threads=…) path never had this problem.time_marker_every_seconds = 5 in processor_config.json), which produced
systematically finer segmentation. Audio, mel features and weights were
verified bit-identical; the corrected prompt restores byte-exact parity.runner.py --engine interpreter: reference implementation.runner.py --engine compiled and engine_cpp/moss_td_engine.cc: the KV
cache lives in LiteRT TensorBuffers aliased as input AND output of every
prefill/decode call (no host KV traffic). Host x86 zh90s q8:
72 s / 7.9 GB (interpreter) -> 43.9 s / 4.0 GB (Python compiled) ->
C++ engine 2.08 GB; int4-b32 C++ engine 1.51 GB peak —
equal peak RSS to the C++ GGUF q4mix build (1.50 GB) with higher transcript
fidelity.