After Kokoro and Chatterbox shipped, the last honest gap left in OpenAudio Suite’s audio stack was Sound Effects. Describe a sound, click synthesize, and what you got back wasn’t AI-generated at all — it was a client-side Web Audio recipe, keyword-matched against a fixed library of oscillator and noise graphs. Real, usable audio, but not generative in any sense the feature’s name implied.
Stable Audio Open (Stability AI, Stability AI Community License) was the fix — an actual text-to-audio diffusion model, free for commercial use under $1M annual revenue. The deployment pattern was supposed to be a formality by this point. It wasn’t. This is the one where the blocker wasn’t RunPod’s platform or a GPU mismatch — it was the model’s own PyPI package being broken.
The pattern that was supposed to just work
By this point, standing up a new self-hosted model had a checklist: write a Flash @Endpoint function, deploy, smoke-test with a real request, decode the WAV, move on. Kokoro took an afternoon. Chatterbox took three sessions, but every problem was a RunPod/Flash configuration detail — GPU generation mismatches, network volume placement, platform flakiness. Nothing about the model’s own packaging had been a problem yet.
Stable Audio Open broke that streak on the very first deploy attempt.
A PyPI package that cannot be installed
flash deploy failed immediately, across every published version back to 0.0.7:
Reproducing the resolution locally with pip install --dry-run --only-binary=:all: (matching Flash’s actual install invocation) surfaced the real cause:
encodec==0.1.1 has no wheel at all, for any Python version — source distribution only. Flash’s installer is wheels-only, so this dependency can never be satisfied through it. Every version of stable-audio-tools transitively requires it, so every version was uninstallable.
Before assuming this was a dead end, it was worth checking whether encodec was even load-bearing for the model actually wanted. Reading the source showed it’s only imported for a "seanet"-type audio encoder — a different architecture variant than Stable Audio Open 1.0 actually uses ("oobleck"). The dependency the whole package refused to install over was, for our purposes, dead code.
The cheap fixes didn’t work
Flash advertises an --exclude flag for exactly this kind of situation. It didn’t help:
--exclude only prunes packages pip has already resolved and matched by name — it can’t rescue a resolution that fails before it gets that far. Checking GitHub confirmed this wasn’t a one-off: Stability-AI/stable-audio-tools has several open issues about exactly this install failure. The community workaround, per those threads, is patching setup.py by hand.
Forking, and a pleasant surprise
Before writing a patch from scratch, it was worth checking whether upstream had already fixed this on an unreleased branch. It had — main (unpublished as 0.0.20) had moved encodec, pandas, and the rest of the training-only packages into an optional [train] extra, leaving the base install genuinely lean.
Forked Stability-AI/stable-audio-tools, pointed the Flash worker at stable-audio-tools @ git+https://github.com/<fork>/stable-audio-tools.git instead of the PyPI release. Not “patch a broken package” — “install from the branch that already isn’t broken.”
Three more issues, on the way to a clean install
Progress, not victory yet. requires-python = ">=3.10,<3.11" turned out to be an artificial upper bound — Flash’s --python-version flag controls which wheel tags the installer targets, not the actual interpreter pip runs its own version check against, which stayed the local machine’s real 3.12.3 no matter what was passed. Relaxed to >=3.10 on the fork. PyWavelets==1.4.1 and sentencepiece==0.1.99 had the same “no wheel for 3.12” problem in a milder form — relaxed both to open-ended minimums.
Then, even past all of that, model loading still failed:
pytorch_lightning had been correctly moved to the optional [train] extra — but models/lora/__init__.py unconditionally imports a file that imports it at module top-level. Any inference-time import walks through this file whether or not you’re training a LoRA. Upstream moved the dependency but not the import that required it.
Adding pytorch_lightning back to the base install didn’t actually work, even though it’s a pure-Python wheel that should install anywhere — possibly Flash’s torch-family auto-exclusion substring-matches the name, though that’s unconfirmed. The real fix: wrap the import in try/except ImportError so training-only code degrades gracefully instead of breaking every inference-time import that happens to walk through the same file.
A gotcha that had nothing to do with any of that
With the package problem solved, deployment still failed once more — this time with Kokoro and Chatterbox’s own dependencies tangled into the error:
The new worker file had been added to the same project directory as the other two models. Each resource is supposed to get its own isolated dependency build, and that had held with two resources — adding a third, with a different Python-version requirement, seems to have leaked shared project-level state across builds. The fix was structural: give Stable Audio Open its own Flash project directory, deployed as its own separate app. Problem gone.
A working model, with a bug of its own
Generation finally completed and returned a valid, decodable WAV. Except it was 47.5 seconds long. The request had asked for 4.
generate_diffusion_cond() always produces audio at the model’s full native sample size — a fixed diffusion grid corresponding to roughly 47 seconds — regardless of requested duration. The conditioning’s "seconds_total" only steers content, not tensor length. Fixed by trimming the output to duration × sample_rate samples after generation. A second test confirmed it: requested 4 seconds, got back exactly 4.000.
Where it landed
tts_stable_audio.py mirrors the pattern from both prior models exactly — RunPod-only, no local fallback, a clear error instead of a silent failure if unconfigured. POST /api/sfx/generate now does real synthesis when live, and returns a 503 — not a 500 — when it isn’t, so the frontend can fall back to the original client-side procedural synth instead of failing outright. Results are labeled “Generative” or by procedural recipe name, so nothing pretends to be more than it is.
Verified to the same standard as the previous two: mocked unit tests, a live integration test that skips cleanly with no endpoint configured, and a full browser pass — real generated audio, correctly labeled, zero console errors. Torn down immediately after, same cost discipline as always.
What’s changed each time is which layer breaks. None of these were things a demo notebook would have surfaced — they only show up when you’re running something as an unattended, repeatable, cost-conscious backend service.
Three models in, the RunPod Flash playbook itself has held up well. Kokoro broke on packaging assumptions about CPU vs. GPU images. Chatterbox broke on hardware-generation mismatches and RunPod’s own infrastructure. This one broke on the model’s third-party package simply not being installable as published. Different layer, same discipline: read the actual error, verify before assuming, fix the real thing.