feat(chatterbox-fast): add seed for reproducible one-shot output

TTSRequest gains `seed` (0=random); seeded once per request under the lock via
torch.manual_seed + cuda.manual_seed_all. One-shot output is then byte-reproducible
for a fixed seed+params (verified: seed=42 -> identical sha256 across runs).
Streaming stays non-reproducible by design — adaptive-chunk boundaries depend on
live-measured RTF. Needed for the asset-engine catalog reproducibility contract
(parity with the chatterbox sibling, which exposes seed).
This commit is contained in:
2026-06-02 00:58:57 -07:00
parent 65a0ef67cf
commit 875033ff00
+8
View File
@@ -206,6 +206,7 @@ class TTSRequest(BaseModel):
top_p: float = 0.95
top_k: int = 1000
repetition_penalty: float = 1.2
seed: int = 0 # 0 ⇒ random; a fixed seed repeats a one-shot take (see note below)
# Scheduler overrides (None ⇒ ChunkConfig defaults).
margin: float | None = Field(default=None)
margin_first: float | None = Field(default=None)
@@ -279,6 +280,13 @@ def tts(req: TTSRequest) -> StreamingResponse:
# workload); concurrent callers queue rather than corrupt conditionals.
with engine.lock:
engine._prepare(voice_path, exaggeration=req.exaggeration)
# Seed once per request (under the lock). One-shot is then reproducible
# for a fixed seed + params; streaming is NOT — adaptive-chunk boundaries
# depend on live-measured RTF (wall-clock), so chunk splits vary run to run.
if req.seed:
torch.manual_seed(req.seed)
if DEVICE.startswith("cuda"):
torch.cuda.manual_seed_all(req.seed)
t_req = time.perf_counter()
first_audio_ms: float | None = None