Skip to content

canary: support Indic-Transcribe (Bodhan AI) output modes - #245

Open
TheAnimatrix wants to merge 1 commit into
handy-computer:mainfrom
TheAnimatrix:indic-canary
Open

TheAnimatrix wants to merge 1 commit into
handy-computer:mainfrom
TheAnimatrix:indic-canary

Conversation

@TheAnimatrix

Copy link
Copy Markdown

Summary

Adds support for Bodhan AI's Indic-Transcribe models (Flex/Core, both canary-1b-v2 fine-tunes for 27 Indian languages) to the canary arch.

Their canary2 prompt has a tenth slot after <|itn|>: <|romanized|> / <|noromanized|>. Without it the prompt comes out one token short and the output mode can't be picked. The model's mixed-script output (Indian words in their script, English words and digits in Latin) is selected through the <|itn|> slot, which canary currently hardwires to <|noitn|>.

When a GGUF carries stt.canary.special.romanized_id / noromanized_id:

  • the canary2 prompt grows the romanized slot (canary2_base_prompt_tokens counts it)
  • transcribe_run_params::itn picks the mode: ON = mixed, OFF = native. TRANSCRIBE_FEATURE_ITN is advertised for these models only
  • a <code>-Latn language (BCP-47 script subtag, e.g. te-Latn) selects romanized output. The converter lists these codes in general.languages so the dispatcher's language gate accepts them; they map to the base code's token
  • stt.canary.default_mode (native | mixed | romanized) is the mode used when itn is DEFAULT

Stock canary GGUFs have none of these keys, so they keep the nine-slot prompt and the fixed <|noitn|>.

scripts/convert-indic-canary.py converts the published .nemo checkpoints without a NeMo install. It rebuilds cfg, state_dict() and the aggregate tokenizer (Bodhan's CanaryMultilingualTokenizer: spl_tokens + one shared multilingual sub-tokenizer) from the archive, then hands off to convert-canary.py's convert().

Scope

  • src/arch/canary/weights.{h,cpp}: optional romanized_id, noromanized_id, default_mode hparams
  • src/arch/canary/model.cpp: prompt slot, output-mode selection, -Latn lookup, ITN feature bit
  • scripts/convert-indic-canary.py: new converter wrapper

No changes to other families, the ABI, or the public header.

AI Assistance

Opus 5.5 high was used in assessing bugs and planning fixes.

Model files for testing

The published checkpoint is gated on Hugging Face. An ungated Q8_0 conversion made with this script is at https://huggingface.co/TheAnimatrix/indic-transcribe-flex-gguf (sha256 807af7f8…dd3282), with the license and attribution.

Validation

  • Converted bodhan-ai/indic-transcribe-flex nemo/indic_transcribe_flex.nemo (rev ecba9ba): all 1892 state-dict tensors consumed, none left over. The token table matches the checkpoint's tokenizers. F32 and Q8_0 (transcribe-quantize --quant Q8_0) both tested.

  • On a 20 s Telugu/English code-mixed clip (Metal, M4), the output matches the model's own PyTorch reference (indic_transcribe.py) word for word in all three modes, with the same prompt ids ([7, 4, 18, 187, 187, 5, 8|9, 10|11, 13, 15]):

    • mixed: -l te
    • native: -l te --no-itn
    • romanized: -l te-Latn

    Q8_0 runs 0.75 s for 19.8 s of audio.

  • Regression: canary-180m-flash Q8_0 gives an identical transcript with and without this change.

  • Not run: the WER pipeline or numerical tensor validation against a NeMo reference dump. Happy to add tests/goldens if you want them for this variant.

Indic-Transcribe-Flex/Core are canary-1b-v2 fine-tunes whose prompt has a
tenth slot after <|itn|>: <|romanized|>/<|noromanized|>. When a GGUF carries
stt.canary.special.romanized_id/noromanized_id the canary2 prompt grows that
slot, transcribe_run_params::itn selects mixed-script output (FEATURE_ITN is
advertised for these models), a "<code>-Latn" language selects romanized
output, and stt.canary.default_mode picks the mode used when itn is DEFAULT.
Stock canary GGUFs keep the nine-slot prompt and fixed <|noitn|>.

scripts/convert-indic-canary.py converts the published .nemo checkpoints
without a NeMo install by rebuilding cfg, state_dict and the aggregate
tokenizer from the archive and reusing convert-canary.py's convert().
@TheAnimatrix
TheAnimatrix requested a review from cjpais as a code owner November 4, 2026 09:46

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant