By the time Kokoro, Chatterbox, and Stable Audio Open had shipped, three of OpenAudio Suite’s four AI capabilities were self-hosted. The fourth wasn’t. /api/transcribe and /api/voices/analyze were both still handing audio straight to openai/whisper-1 through OpenRouter — a paid, hosted call, sitting right next to three engines that had spent real effort moving off exactly that pattern. Whisper’s architecture is open. What we were calling wasn’t.
The fix that closed this gap also happened to close a second one. IndicWhisper (AI4Bharat, IIT Madras) is a Whisper checkpoint fine-tuned specifically on Indian languages, and its license — MIT, explicitly stated to cover the fine-tuned weights, not just the training code — put it in rare company after a licensing scan that had ruled out most of the well-known open audio models over the preceding weeks. One model, two gaps closed. It just didn’t deploy the way the plan assumed it would.
The checkpoint that isn’t on HuggingFace
The original plan, written before any of this was checked, assumed faster-whisper or whisper.cpp loading a HuggingFace checkpoint — the same shape as every other self-hosted model so far. Reading AI4Bharat’s actual repository turned up a maintainer comment that changed the plan before a line of worker code was written:
The real checkpoints are direct zip downloads from AI4Bharat’s own object store, one file per language. Hindi’s is 4.26GB. And the format inside is a standard transformers-pipeline checkpoint, not CTranslate2 — so faster-whisper would have needed an unverified conversion step for no clear benefit. The actual plan became: load it with transformers.pipeline(), exactly the way AI4Bharat’s own inference snippet does it, and cache the unzipped checkpoint on a RunPod network volume so the 4.3GB download only happens once.
A generation config from before timestamps existed
The checkpoint downloaded, extracted, and loaded cleanly — a good sign that turned out to be premature. The first real transcription request failed immediately:
This IndicWhisper checkpoint predates the generation-config fields transformers now requires for return_timestamps=True — a known gap for older or custom Whisper fine-tunes, not specific to this model.
Replace the pipeline’s generation_config with a fresh one borrowed from the base model IndicWhisper was fine-tuned from (GenerationConfig.from_pretrained("openai/whisper-medium")), then reapply Hindi’s forced_decoder_ids on top of that borrowed config — not on model.config, which is where the original code had set it and where newer transformers no longer looks for it at generation time.
RunPod’s undocumented-outside-a-skill 60 second ceiling
With the pipeline fixed, deploying for real (flash deploy, not just flash dev) surfaced a second failure — on both the fallback test and the live test, every time:
The client code called RunPod’s /runsync endpoint with a generous 1800-second timeout on the HTTP request itself. That timeout was irrelevant. /runsync has its own hard 60-second ceiling on RunPod’s side, enforced regardless of what the caller passes — and IndicWhisper’s cold start, mostly spent pulling the container image, had already measured at roughly 104 seconds on its own during earlier testing. No client-side timeout setting could fix a server-side one.
Switch to async /run plus a poll loop against /status/{job_id}, instead of /runsync. Every prior model’s cold start happened to land under 60 seconds, which is exactly why this hadn’t come up yet — it isn’t a bug in the other three, it’s a limit that only bites once a cold start runs long enough to hit it.
Closing the loop on itself
Testing self-hosted Hindi transcription needs Hindi audio, and getting some turned out to be free: Kokoro had picked up four unused Hindi voices in the same multilingual push that motivated this model, shipped a session earlier as a one-line catalog fix. Generating a clip with Kokoro’s hf_alpha voice and feeding it straight into IndicWhisper meant the whole test was self-contained — no external sample, no manual recording, just one shipped feature verifying the next one.
Where it landed
stt_indicwhisper.py follows the RunPod-only pattern from Chatterbox — no CPU fallback, since a ~769M-parameter whisper-medium fine-tune isn’t CPU-viable the way Kokoro’s 82M parameters are. /api/transcribe gained a language parameter: hi routes to IndicWhisper when the endpoint is configured, and falls back to the existing OpenRouter path otherwise — the same graceful-degradation shape as every other engine in this stack, never a hard failure just because a self-hosted option isn’t live right now.
Verified end to end: the pytest suite green in both configured and unconfigured states, the Kokoro-to-IndicWhisper round trip above, and a full browser pass through the actual Transcribe page — upload, Hindi toggle, a correct transcript with the right language badge and timestamps, zero console errors. And, predictably by now, flash dev had again left a second real billing endpoint running that needed deleting separately — the same gotcha first found deploying Kokoro, still worth checking for every single time. Both endpoints and the network volume were torn down immediately after verification, same as every model before it.
The checklist holds up. What changes each time is which assumption in it turns out to be wrong for this particular model.
Four models into this stack, the shape of the work has stopped changing even as the specific bugs keep being different ones. Kokoro broke on a CPU/GPU packaging assumption. Chatterbox broke on hardware generations and RunPod’s own infrastructure. Stable Audio Open broke on a third-party package that simply wasn’t installable. This one broke on a stale generation config and an undocumented timeout — neither of which a demo script calling the model directly would ever have hit, because a demo script doesn’t need to survive a cold start on someone else’s infrastructure.