Reduce XTTS tail hallucinations

This commit is contained in:
Mikei386
2026-09-04 08:32:45 +02:00
parent be8a654f1e
commit 8dac735680
2 changed files with 43 additions and 21 deletions
+31 -19
View File
@@ -318,12 +318,12 @@ def clean_for_speech(text: str) -> str:
def _split_chunk(text: str, limit: int = CHUNK_CHARS) -> list[str]:
"""Return sentence-sized XTTS requests with a conservative hard ceiling.
"""Return paragraph-sized XTTS requests with a conservative hard ceiling.
A sentence is deliberately never combined with the following sentence.
Overlong sentences are split at clause boundaries first and at words only
as a last resort. This preserves XTTS prosody without exposing it to an
unbounded paragraph.
Complete neighbouring sentences are combined while they fit. This avoids
restarting the generative XTTS decoder after every short sentence, which
can create invented tail syllables between sentences. Overlong sentences
are split at clause boundaries first and at words only as a last resort.
"""
text = text.strip()
if not text:
@@ -331,38 +331,50 @@ def _split_chunk(text: str, limit: int = CHUNK_CHARS) -> list[str]:
sentences = re.split(r"(?<=[.!?])\s+|\s+(?=\d+[.)]\s)", text)
chunks: list[str] = []
current = ""
for sentence in sentences:
sentence = sentence.strip()
if not sentence:
continue
if len(sentence) <= limit:
chunks.append(sentence)
candidate = f"{current} {sentence}".strip()
if current and len(candidate) > limit:
chunks.append(current)
current = sentence
else:
current = candidate
continue
if current:
chunks.append(current)
current = ""
# Retain commas in the preceding clause so XTTS can reproduce the
# intended pause. Semicolons and colons were normalized earlier.
clauses = re.split(r"(?<=,)\s+", sentence)
current = ""
long_current = ""
for clause in clauses:
clause = clause.strip()
candidate = f"{current} {clause}".strip()
if current and len(candidate) > limit:
chunks.append(current)
current = ""
candidate = f"{long_current} {clause}".strip()
if long_current and len(candidate) > limit:
chunks.append(long_current)
long_current = ""
if len(clause) <= limit:
current = f"{current} {clause}".strip()
long_current = f"{long_current} {clause}".strip()
continue
# A clause without a usable pause can still exceed the ceiling.
for word in clause.split():
candidate = f"{current} {word}".strip()
if current and len(candidate) > limit:
chunks.append(current)
current = word
candidate = f"{long_current} {word}".strip()
if long_current and len(candidate) > limit:
chunks.append(long_current)
long_current = word
else:
current = candidate
if current:
chunks.append(current)
long_current = candidate
if long_current:
chunks.append(long_current)
if current:
chunks.append(current)
return chunks