SnackOnAI Engineering | Senior AI Systems Researcher | Technical Deep Dive | November 12, 2026
The transcription half of that bet is well supported: within 0.16 WER points of Sarvam's paid API across 12 languages, at 0.46B parameters. The decision half rests on 247 test clips, most of them acted, in synthetic voices. It might be the most useful idea in Indian voice AI this year. Right now it's a demo.
The promise: by the end you'll know how Hima-ASR is built from parts of Qwen3-ASR and Gemma 3, where its "460M" really comes from, how strong its WER claims are, and what it would take to trust its decision head in a call center.
The fence: we read the Hima GitHub repository in full (11 commits, October 12 to 13, 2026: model, bridge, writer, CTC helper, data pipeline, training loop) and the Hima-ASR model card (evaluation, decision head, deployment tables). The GitHub code predates the published model. Its README still says "no result is claimed yet" and lists the decision head as "later," and the decision head's training code, and the ONNX and MLX ports, aren't in the public repo. The training-data breakdown in this issue's brief doesn't appear on the model card, so we treat it as supplied context and check it for internal consistency only. All benchmark numbers are the author's own runs. We didn't run the model.
What It Actually Does
Hima-ASR transcribes 16 kHz mono speech, 1 to 30 seconds per clip, in Assamese, Bengali, Gujarati, Hindi, Kannada, Malayalam, Marathi, Odia, Punjabi, Tamil, Telugu and English, in native script or in a "mixed" style for code-switched speech. A separate 17.5M-parameter decision head answers questions about the same clip ("Does the caller want a human agent?", "How urgent is this?") as a probability for each option.
Part | Source | Role |
|---|---|---|
Audio encoder | Qwen3-ASR-1.7B's audio tower, trained further (Apache 2.0) | 128-band log-mel at 100 columns/s → 1,024-dim frames at 12.5/s |
Projector ("bridge") | New | Linear 1,024 → 1,024 → GELU → 640 → LayerNorm |
Text decoder ("writer") | Gemma 3 270M, vocabulary trimmed 262,144 → 63,335 (Gemma Terms of Use) | Greedy, piece by piece, with loop and length guards |
CTC "vote" layer | New, training only | Auxiliary loss, weight 0.3 |
Decision head | New, 17.5M | Mixes all encoder and decoder layers, attends over audio frames, scores options |
Total | ~0.46B ASR + 17.5M head, 920 MB in bf16 |
Runtime | Size | Speed (× real time, M4 Pro) |
|---|---|---|
PyTorch CPU | 920 MB | 16× |
ONNX int8 CPU | 487 MB | 35× |
ONNX q4 CPU | 360 MB | 31× |
MLX fp16 (Mac GPU) | 1.0 GB | 63× |
MLX 4-bit (Mac GPU) | 379 MB | 73× |
Think of a court stenographer with a second job. While typing every word, the stenographer also notes the speaker's tone, and afterwards can answer checkbox questions about it. Both answers come from the same listening.
The Architecture, Unpacked

What to notice: the encoder runs once. The transcript and the decisions both read its frames, and the decision head also reads the decoder's states, so it sees sound and words together.
Four decisions define the model.
One: borrow a strong encoder, shrink the decoder. Qwen3-ASR's audio tower already handles 1,024-dim acoustic representations. Gemma 3 270M is the smallest decoder in a large multilingual family. The DECISIONS log is explicit: "the aim is a small model."
Two: trim the vocabulary. Gemma's 262,144-piece table becomes 63,335 pieces. At width 640, that removes about 127M parameters, more than a quarter of the final model.
Three: train everything jointly, with a CTC crutch. Encoder, bridge and writer learn together under the writer's cross-entropy loss. A CTC "vote" layer must read text from the frames alone. In a small test it improved the writer's character error by 8 points (69% → 61% early in training).
Four: one encoding, two consumers. The decision head is a small attention module over the frames plus a learned mix of hidden layers, so it can hear prosody without transcribing first.
The Code, Annotated
Snippet one: how sound becomes a prompt (excerpt from src/hima/writer.py, our comments marked # SnackOnAI:).
class Bridge(nn.Module):
"""frame of the ears -> a vector that the writer can read"""
def __init__(self, ears_width, writer_width, hidden=1024):
super().__init__()
self.layers = nn.Sequential(nn.Linear(ears_width, hidden), nn.GELU(),
nn.Linear(hidden, writer_width), nn.LayerNorm(writer_width))
# SnackOnAI: ~1.7M parameters translate 1,024-dim audio frames into Gemma's 640-dim word space
def rows(self, frames, counts):
"""For each clip: the vectors [start] + its frames + [now write]."""
sound = torch.split(self.bridge(frames), counts.tolist())
start = self.piece_vectors(torch.tensor([self.start_id], device=frames.device))
return [torch.cat([start, s.to(start.dtype), self.now_write[None].to(start.dtype)]) # ← THIS is the trick:
for s in sound] # audio frames are fed
# as if they were words
No cross-attention, no special decoder. Gemma 3 270M simply reads 125 projected audio frames as its prompt, then a learned "[now write]" vector, then writes. That's why a 270M text model can become an ASR decoder with one small bridge.
Snippet two: guarding a small greedy writer (excerpt, same file).
limits = [min(most_pieces, int(pieces_per_frame * int(n)) + 8) for n in counts]
# SnackOnAI: from the docstring: "a true text needs up to about 1.0 piece for each frame in the densest language,
# and under 0.75 in the others. A first limit of 0.75 cut the end off good clips of that language. 1.5 leaves room."
...
written[i].append(int(choice[i]))
if len(written[i]) >= limits[i]:
done[i] = True
elif stop_loops and is_stuck(written[i]): # ← THIS is the trick: one piece 6 times, or a
written[i] = cut_loop(written[i]) # unit of 2 to 8 pieces 4 times, ends the clip
done[i] = True
Small decoders hallucinate by repetition. Hima caps output by audio length and cuts loops after the fact. That's pragmatic, but it also means a speaker who genuinely repeats a word six times gets trimmed.
Snippet three: the CTC helper that makes the encoder honest (excerpt from src/hima/ears.py).
class VoteLayer(nn.Module):
"""frames -> for each frame, a score for every piece in our table"""
def __init__(self, width, number_of_votes, hidden=1024, dropout=0.1):
super().__init__()
self.layers = nn.Sequential(nn.Linear(width, hidden), nn.GELU(), nn.Dropout(dropout),
nn.Linear(hidden, number_of_votes))
def vote_loss(scores, counts, targets, target_lengths):
per_clip = F.ctc_loss(scores, targets, counts, target_lengths, blank=0, reduction="none", zero_infinity=True)
return per_clip / target_lengths.clamp(min=1) # ← THIS is the trick: the vote layer has NO language model
# to lean on, so its loss forces the frames themselves to
# carry the text. Weight 0.3 vs 1.0 for the writer.
This also helps the decision head, which reads the same frames. An encoder that has to support a context-free CTC reader learns frames that carry real acoustic content, not just hints for the decoder.
Snippet four: using it (entry points as documented on the model card; arguments shown are the card's).
import torch
from transformers import AutoModel
hima = AutoModel.from_pretrained("Prahaladha/hima-asr", trust_remote_code=True, dtype=torch.float32)
text = hima.transcribe("call.wav", language=None, # None = automatic language detection
style="mixed", # "mixed" for code-switched Hinglish, "native" for native script
batch_size=16)
# Joint run: one encoding → transcript + decisions (16 built-in questions from decide/questions.json by default)
from hima import Hima # remote-code module, per the card
out = Hima("both").run("call.wav") # ← THIS is the point: one encoder pass feeds both outputs
print(out["text"], out["decisions"])
The card's own decide/questions.json defines the default questions. Custom questions take a type (choice, yesno, scale) and options with optional descriptions.
It In Action
Input: a 10-second Hinglish support call: "mujhe refund chahiye, abhi, please kisi insaan se baat karao."
Step one, features. 160,000 samples become a 128 × 1,000 log-mel picture (100 columns per second).
Step two, encode once. The encoder produces 125 frames of 1,024 dimensions (12.5 per second).
Step three, transcribe. The bridge maps the frames to 640 dimensions, and the writer reads [bos] + 125 frames + [now write], a 127-vector prefix, then writes greedily. The length cap is min(512, 1.5 × 125 + 8) = 195 pieces, far above what a 10-second sentence needs.
Step four, decide. The decision head mixes encoder and decoder layers and scores the 16 default questions. The card's examples report probabilities like "100%" for the top option and "0 to 2%" for the rest.
Output (speed arithmetic from the card's × real-time figures; decisions illustrative):
Runtime | 10 s clip takes | Size |
|---|---|---|
PyTorch CPU (16×) | ~0.63 s | 920 MB |
ONNX int8 CPU (35×) | ~0.29 s | 487 MB |
MLX 4-bit (73×) | ~0.14 s | 379 MB |
self-reported WER, same clips and scorer (lower is better)
Hima 0.46B Flex 1.2B Core 1.2B Sarvam v3 (API)
all 12 langs 12.95 13.23 12.84 12.79
FLEURS avg 12.7 12.7 12.3 12.0
Tamil 17.4 20.9 19.8 20.5 ← Hima's best margin
English 7.5 6.4 6.9 4.0 ← Hima's worst gap
Hindi Kathbath 3.4 2.5 n/a n/a
At a third of the competing open models' size, Hima matches them overall. It leads clearly in the four Dravidian languages and trails noticeably in English, which matters for code-mixed calls.
Why This Design Works (and what it trades away)
It works because the encoder already knows how to listen. Qwen3-ASR's audio tower was trained by Alibaba at scale. Hima teaches it Indian languages slowly, with a learning rate of 3e-5 and the encoder frozen for the first 2,000 steps, rather than learning acoustics from scratch.
It works because language balancing is explicit. The sampler draws a language with weight hours^0.5, so Hindi's volume doesn't drown out Assamese or Odia. That's likely why the low-resource Dravidian results are strong.
It works because decisions reuse computation. The head adds 17.5M parameters, 3.8% of the ASR model, and no second encoder pass. A relay pipeline needs a separate classifier or LLM call per decision.
It trades away English. 7.5 WER against Sarvam's 4.0 is nearly 2x worse. On Indian support calls, where English product names and numbers interleave with Hindi, that gap shows up in exactly the words agents act on.
It trades away long audio. Clips must be 1 to 30 seconds, so calls need external segmentation, and the decision head sees one segment at a time, never the whole conversation.
It trades away an independent quality signal. Every WER number comes from the author's own runs. The test-set breakdown is given, but nothing has been reproduced elsewhere.
It trades away a clean license. The writer is Gemma-derived, so the whole checkpoint carries the Gemma Terms of Use.
Technical Moats
The data, if the supplied breakdown is right. The brief lists about 181K hours: 132,130 scraped and relabelled, plus 49,061 across 34 open datasets (22,121 typed by people, 13,153 read from scripts, 13,787 machine-aligned). The open-dataset figures add up exactly. Assembling and cleaning that much Indic audio, including telephony sets like Gram Vaani and Equal AI Hindi Calls, is the real work. The model card doesn't publish this table, so its link to the released checkpoint is unconfirmed.
The integration choices. CTC as a helper, staggered learning rates per component, loop-aware decoding and per-language length limits are small decisions that add up to a working small model.
Not the architecture. Encoder plus projector plus small LLM is the standard speech-LLM recipe. Anyone with Qwen3-ASR, Gemma and data can rebuild it.
Contrarian Insights
One: the decision head's evaluation is a demo, not a benchmark. It's scored on 247 clips in total (236 correct, 95.5%), with categories of 12 to 36 clips. The caller clips are acted replies in synthetic voices; only the read-speech clips are real (FLEURS). With 27 of 29 correct on urgency, the 95% confidence interval runs from about 78% to 98%. More importantly, there's no ablation against the obvious baseline: transcribe, then ask a text model. Questions like "talks about money" or "mentions a date" are answerable from words alone. The head's real claim, that it hears tone and urgency the transcript misses, is the one the evaluation doesn't isolate.
Two: "460M" is a 600M model with a smaller dictionary, and the training data needs auditing. The project's own decision log says the model "stays near 600 million weights." Trimming Gemma's vocabulary from 262,144 to 63,335 pieces removes 127M parameters, which accounts for the difference. That's a legitimate efficiency gain, but it's not architectural compactness. Separately, the supplied data list is 73% scraped audio with machine relabelling, and only 12% human-typed. It also includes Kathbath (1,270 h) and FLEURS (94 h), the families of two headline evaluation sets. Training on their train splits is normal, but whether the test speakers were held out isn't documented on the card. The public code holds out 1% of speakers per shard, which says nothing about official test splits.
Surprising Takeaway
The single biggest size reduction in Hima-ASR isn't a new architecture. It's deleting 76% of Gemma 3's vocabulary. Cutting the table from 262,144 to 63,335 pieces removes about 127M parameters, seven times the size of the entire decision head and over a quarter of the final model. For any small speech or language model aimed at a specific set of scripts, trimming the vocabulary is the cheapest compression there is, and it costs nothing in quality for languages you don't plan to write.
TL;DR For Engineers
Hima-ASR = Qwen3-ASR-1.7B's audio encoder + a 1,024 → 640 projector + Gemma 3 270M with a trimmed 63,335-piece vocabulary, about 0.46B parameters, plus a 17.5M decision head reading all layers.
Self-reported WER: 12.95 overall across 12 languages vs Sarvam v3 at 12.79. It leads in Tamil, Telugu, Kannada and Malayalam and trails in English (7.5 vs 4.0).
It runs at 35× real time on CPU with ONNX int8 (487 MB) and 73× on a Mac with MLX 4-bit (379 MB).
The decision head is promising but scored on 247 mostly synthetic clips, with no transcript-only baseline. Validate it on your own calls before routing on it.
Gemma's Terms of Use apply to the whole checkpoint, and the decision head's training code isn't public.
Explain It Like I'm New
Speech recognition turns spoken words into text. For a customer support system, that's only half the job. The system also needs to know whether the caller is upset, whether it's urgent, and whether they want to speak to a real person.
The usual approach is a relay. One AI writes down what was said, and a second AI reads the text and makes judgments. The problem is that text loses a lot. "Fine" can be calm or furious depending on how it's said.
Hima-ASR tries to do both jobs in one model. Its listening part turns sound into an internal representation. A small writing part turns that into text, in eleven Indian languages and English. A small judging part looks at the same representation and answers multiple-choice questions about the call, with a confidence for each answer.
Because the listening only happens once, this is cheaper and faster than a relay, and in principle the judging part can hear tone, not just words. The transcription has been measured carefully against strong competitors. The judging part has so far only been tested on a small set of mostly acted examples.
If that judging part holds up on real calls, voice assistants in multilingual countries could react to how people feel, not just what they say.
See It In Action
Hima-ASR model card, Prahaladha on Hugging Face. Read the examples and code. Full usage for transcription, the joint transcript-and-decisions call, custom questions, and the per-test-set WER tables.
Hima training code, Prahaladha-Reddy on GitHub. Read the source. A plain-English, heavily commented implementation of the encoder, bridge, writer and CTC helper. One of the most readable speech-LLM codebases around.
Building Robust and Scalable Multilingual ASR for Indian Languages, IIT Madras SPRING Lab. Read the paper. Context on Indian-language ASR, where spontaneous speech WER (~59%) dwarfs read speech (~17%).
Community Conversation
Prahaladha Reddy (author), DECISIONS.md. A rare public decision log: why Gemma 3 270M, why Qwen3-ASR's encoder, why joint training, and the measured 8-point gain from the CTC helper. Open questions include number formatting and which script English words should use.
Arjun Gangwar, Kaousheik Jayakumar and S. Umesh (IIT Madras), MADASR 2.0 paper. A useful reality check: on spontaneous Indian speech, baseline WER sits near 59% against 17% for read speech. Benchmarks dominated by read speech can flatter any model.
Google, Gemma Terms of Use. The terms that flow through Hima's writer: use restrictions under Google's Prohibited Use Policy, a requirement to pass those restrictions on to anyone you redistribute to, and notice obligations. Commercial use is allowed. "Apache 2.0" it is not.
Conclusion: A Competitive Indian ASR Today, a Decision Engine Someday
Hima-ASR is a strong, honest, small speech model: within 0.16 WER points of a paid API across 12 languages, best-in-class in the Dravidian languages on its own tests, and runnable at 35× real time on a laptop CPU. Its decision head is the right idea for voice agents, since one encoding should feed both what was said and how, but 247 mostly synthetic clips with no transcript-only baseline aren't evidence yet. Use Hima for Indian-language transcription now, especially Tamil, Telugu, Kannada and Malayalam. Run the decision head in shadow mode on your own calls before letting it route a single customer, and have counsel read the Gemma terms before you redistribute it.
References
Qwen3-ASR Technical Report, Qwen Team. The source of Hima's audio encoder.
Gemma 3 Technical Report, Gemma Team. The family behind Hima's 270M writer and its 262K-piece vocabulary.
Connectionist Temporal Classification, Graves et al. The alignment-free loss behind Hima's training-only vote layer.
SUPERB: Speech processing Universal PERformance Benchmark, Yang et al. Why learned mixes of all hidden layers beat any single layer for speech tasks beyond ASR, the idea behind Hima's decision head.
Building Robust and Scalable Multilingual ASR for Indian Languages, Gangwar, Jayakumar, Umesh. Read vs spontaneous Indian ASR, for calibrating benchmark numbers.
Summary
Hima-ASR combines Qwen3-ASR's audio encoder with a trimmed-vocabulary Gemma 3 270M writer and a 17.5M decision head that reads the same encoding. It reaches 12.95 self-reported WER across 12 languages at 0.46B parameters and runs at 35× real time on CPU. Its decision head is promising but tested on only 247 mostly synthetic clips.
Sponsored Ad If you enjoy practical AI insights, check out SnackOnAI and support the newsletter by subscribing, sharing, and exploring our sponsored ad—it helps us keep building and delivering value 🚀
It's time to set up your holiday streaming campaign
Are you ready to reach holiday shoppers on CTV? For many streamers, the season is already underway.
Last year, according to Roku internal data, holiday content searches on Roku saw an 86% increase during October. And in November, those searches rise an average of 39% weekly. This surging holiday spirit is mirrored by consumer activity, as about half of Roku streamers shopped Black Friday and Cyber Monday deals in 2025.
Meeting those shoppers is easier than you might think. Begin with the seasonal outcome you hope to drive: awareness, online sales, leads, store visits, or another measurable goal. From there, define your audience, upload your creative, set the budget and campaign dates, and establish measurement before launching. You can even create a shoppable ad unit that lets streamers buy with a click of their remote.
Our holiday playbook offers a month-by-month guide to reaching shoppers during this all-important time of year.
First-time spenders get a $5,000 match on the first $5,000 spent with code BEEHIIV5K. Terms apply.



