Indic-Transcribe Flex β€” CoreML

A CoreML port of bodhan-ai/indic-transcribe-flex (1.22B params, FastConformer encoder + Transformer decoder, fine-tuned from nvidia/canary-1b-v2), converted for on-device inference on Apple Silicon (macOS 15+). Produced for Veere as Task 84 of an internal engineering plan β€” a spike to see whether this model could run as Veere's background re-transcription pass without leaving the device.

Built with Indic-Transcribe-Flex from Bodhan AI / AI4Bharat.

What this is

Three CoreML models (.mlpackage, fp16 compute), converted directly from the original fp32 checkpoint with a from-scratch conversion script β€” no existing Canary CoreML port covers this architecture's decoder (see "Provenance" below):

File Role I/O
indic_flex_preprocessor.mlpackage mel front-end (STFT + filterbank + per-utterance normalization) β€” FLOAT32 compute precision, matching the original's own "runs in fp32 regardless of model dtype" audio [1,480000] f32 (30s @ 16kHz, pad short audio with trailing silence) + sample_lens [1] i32 β†’ features [1,128,3001] f32, feat_lens [1] i32
indic_flex_encoder.mlpackage FastConformer encoder, 32 layers input_features [1,128,3001] f32 + attention_mask [1,3001] i32 β†’ encoder_states [1,376,1024] f32, encoder_lengths [1] i32
indic_flex_decoder_stateful.mlpackage Transformer decoder, 24 layers, cross-attention to the encoder output, autoregressive with a CoreML State self-attention KV cache (macOS 15+) input_ids [1,Q] i32, position_ids [1,Q] i32, encoder_states [1,376,1024] f32, cross_mask [1,1,1,376] f32, self_attn_mask [1,1,Q,end_step] f32 (Q, end_step: RangeDim) β†’ logits [1,Q,7152] f32; 48 State tensors (self_k_cache_0..23, self_v_cache_0..23, each [1,8,320,128] fp16)
vocab.json flat, id-indexed list of 7,152 SentencePiece piece strings detokenize with ''.join(pieces).replace('▁', ' ').strip() β€” no SentencePiece binding needed
tokenizer_config.json copied verbatim from the source checkpoint: special token ids + the fixed 10-token canary2 prompt per language e.g. hi native = [7, 4, 18, 89, 89, 5, 9, 11, 13, 15]; romanized swaps index 7 from 11 to 10
Indic_Open_Model_License.md the license text, verbatim required by the license's Distribution clause (Β§5.3)

Fixed 30-second input shape. Every chunk must be exactly 480,000 samples (30s @ 16kHz); pad shorter audio with trailing silence and pass the true sample count in sample_lens. This matches the checkpoint's own training max_duration and the chunking already used to measure its real accuracy (see "Measured numbers" below) β€” the original PyTorch wrapper explicitly does not chunk internally and degrades sharply past ~45s, so a caller must chunk before calling this model, not after.

Cross-attention is recomputed every decode step, not cached as a second State. The original model caches cross-attention K/V once per utterance; since it is a pure, deterministic function of encoder_states (fixed for the whole utterance), recomputing it every step is numerically identical and removes the one piece of this conversion that would have needed step-conditional state writes β€” torch.jit.trace cannot express those at all. See the conversion script's module docstring for the full reasoning.

Provenance / how this was made

No existing Canary or FastConformer-AED-decoder CoreML port covers this architecture β€” checked against vendor/FluidAudio (no Canary* manager at all: Parakeet/TDT, Qwen3, Cohere, Paraformer, SenseVoice only) and against FluidInference/canary-1b-v2-coreml (the base model's own community port β€” a 4-component split: Preprocessor, EncoderInt4, DecoderInt4, Projection; useful as a component-split precedent, not reused directly here, and not weight-compatible with this fine-tune regardless). The conversion technique β€” register_buffer KV-cache tensors turned into CoreML States via ct.convert(..., states=[ct.StateType(...)]), torch.jit.trace (no data-dependent branching survives State conversion), explicit matmul+softmax attention, .cpuAndGPU compute units β€” is the same one used earlier in this project to port Qwen3-ASR to CoreML (FluidInference's own mobius converter, adapted for a decoder-only model); this port adapts it to an encoder-decoder architecture with real cross-attention, which that precedent did not need to handle.

Conversion script: tools/coreml/convert_indic_transcribe.py. Full technical writeup, including every numeric parity check run before this was trusted: see this project's reviews/2026-09-09-indic-transcribe-coreml-port.md.

Measured numbers (this Mac, 2026-09-09)

  • CoSHE dev cut, script-blind WER: 9.66% (95% CI [7.74, 12.31]) on the first 40 of the corpus's 200 clips, 30s chunking, romanized mode β€” the PyTorch fp16/MPS reference on the identical 40 clips: 11.41% (CI [8.92, 14.46]); the full 200-clip PyTorch figure is 15.56%. The CIs overlap substantially, so read this as "at least as good, plausibly a little better," not a decisive win β€” the full 200 clips were not run for this port (a disclosed time-budget call; the report gives the exact numbers and how to complete it).
  • RTF: 0.42 (40 clips) β€” measured on a machine under real contention (load average up to 12 during the run); worth a clean re-measurement.
  • Peak resident memory: 0.97GB β€” roughly an order of magnitude below the PyTorch fp16/MPS path's own measured 7.3–7.7GB. This is the actual point of this conversion.
  • Bundle size: 2.29GB (fp16, uncompiled .mlpackage; no int4/int8 pass attempted).

Full numbers, methodology, and every blocker hit: reviews/2026-09-09-indic-transcribe-coreml-port.md in this project's repo.

Limitations (carried over from the base checkpoint, unchanged by this port)

  • Single-speaker only. The base model's card documents this; multi-speaker audio needs an external diarizer feeding one speaker turn at a time.
  • No streaming / batch only. Not a fit for a live transcript; this port exists for a background/offline re-transcription pass.
  • Romanization is not standardized by the original authors across Indic languages β€” expect output that doesn't always match a house transliteration convention.

License

Indic Open Model License v1.0 (full text: Indic_Open_Model_License.md in this repo, copied verbatim from Bodhan-AI/bodhan-model-info). This is a derivative of bodhan-ai/indic-transcribe-flex (format conversion + quantization are explicitly derivative-creating acts under the license's own definitions) and is distributed under the same license, as the license's Β§5.1 share-alike clause requires. Key points for anyone building on this repo β€” not a substitute for reading the license itself:

  • Free to use, modify, and redistribute, including bundled inside a commercial on-device product, with an attribution notice ("Built with Indic-Transcribe-Flex from Bodhan AI / AI4Bharat.") and this license text kept alongside it.
  • Any further derivative you make from this repo and distribute must carry this same license (Β§5.1 β€” copyleft/share-alike).
  • Running this model as a hosted API for third parties (as opposed to on-device) needs Bodhan AI's prior written approval (Β§3).
  • The base model nvidia/canary-1b-v2 carries its own separate license β€” ensure your use complies with both.

int8 bundle (int8/, 2026-09-10)

Weight-only linear per-channel int8 of the same fp16 encoder + stateful decoder (preprocessor stays fp16), via coremltools.optimize.coreml. Same 200-clip CoSHE dev set, 30 s chunking, romanised mode:

variant bundle script-blind WER [95 % CI] strict RTF compute units
fp16 (root) 2.29 GB 13.40 % [11.9–14.8] 43.6 % 0.39 CPU_AND_GPU
int8 1.1 GB 14.04 % [12.2–15.9] 43.8 % 0.24 ALL (encoder 98.8 % on the Neural Engine)
int4 (not published) 632 MB 14.42 % [12.9–16.0] 44.5 % 0.37 ALL β€” no gain: the stateful decoder cannot run on the Neural Engine

Load every component with compute_units=ALL: the FastConformer encoder runs almost entirely on the Neural Engine; the decoder's CoreML State-API ops (the KV cache) have no Neural Engine support and run on the GPU. Licence unchanged (Indic Open Model License v1.0, see the licence file at the root). Recipe and measurements: tools/coreml/convert_indic_transcribe.py --quantize int8, tools/coreml/ane_check.py in the Veere repo.

Downloads last month
6
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Model tree for sookie90/indic-transcribe-flex-coreml

Quantized
(2)
this model