fix(melo-test): strip standalone Hangul jamo before synthesis

MeloTTS-Korean's symbol vocab only includes precomposed Hangul
syllables. Bare Hangul Compatibility Jamo characters (ㅋㅋㅋ, ㅠㅠ, ㅎㅎ)
and Hangul Jamo block code points blow up in
`cleaned_text_to_sequence` with `KeyError: 'ㅋ'` (or similar).

Pre-clean the request text in `/tts` to drop those code point ranges
before handing the string to the model, collapsing the leftover
whitespace. If sanitization yields an empty string, return 400 with
an explanation instead of a 500 stack trace.

Also document the limitation in the README troubleshooting section.
This commit is contained in:
Claude Owner
2026-05-26 21:04:27 +09:00
parent 106915b55b
commit 6211985730
2 changed files with 31 additions and 3 deletions

View File

@@ -8,6 +8,7 @@ GET / -> index.html (테스트 UI)
"""
import os
import io
import re
import time
import tempfile
import logging
@@ -62,16 +63,38 @@ class TTSReq(BaseModel):
speed: float = 1.0
# MeloTTS-Korean 의 symbol vocab 은 완성형 음절(가, 나, ...)만 포함한다.
# 'ㅋㅋㅋ', 'ㅠㅠ', 'ㅎㅎ' 같이 한글 자모(Hangul Compatibility Jamo /
# Hangul Jamo) 가 단독으로 들어오면 cleaned_text_to_sequence 에서
# KeyError 로 죽는다. 합성 전에 미리 제거한다.
_STANDALONE_JAMO_RE = re.compile(r"[\u1100-\u11FF\u3130-\u318F\uA960-\uA97F\uD7B0-\uD7FF]+")
def _sanitize_for_melo(text: str) -> str:
# 단독 자모를 공백으로 치환한 뒤 연속 공백을 하나로 정리.
cleaned = _STANDALONE_JAMO_RE.sub(" ", text)
return re.sub(r"\s+", " ", cleaned).strip()
@app.post("/tts")
def tts(req: TTSReq):
model = state.get("model")
if model is None:
raise HTTPException(503, "model not ready")
text = (req.text or "").strip()
if not text:
raw = (req.text or "").strip()
if not raw:
raise HTTPException(400, "empty text")
if len(text) > 500:
if len(raw) > 500:
raise HTTPException(400, "too long (<=500)")
text = _sanitize_for_melo(raw)
if not text:
raise HTTPException(
400,
"text contains only standalone Hangul jamo (e.g. ㅋㅋㅋ); "
"MeloTTS-Korean cannot synthesize these.",
)
if text != raw:
log.info("sanitized standalone jamo: %r -> %r", raw[:60], text[:60])
speed = max(0.5, min(2.0, float(req.speed or 1.0)))
fd, out_path = tempfile.mkstemp(suffix=".wav")