Files
xiaoxia-saas/packages/application/tts_job/text_splitter.py
T
灵应 d6ab413dcd
CI/CD Pipeline / Deploy Staging (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Failing after 47h57m37s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 47h57m37s
feat: P1 长文本分段合成
- 新增 text_splitter.py: 长文本智能分段(句子边界 + 短段合并)
- 新增 audio_merger.py: FFmpeg concat 音频合并器
- workflow.py: 分段合成完整流程(同步合并 / 异步轮询 / 失败处理)
- tts_synthesis.py: 新增 process_tts_segment_synthesis Celery 任务
- tts.py: 路由层自动识别分段任务并分发到对应 Celery task
- 23 个单元测试全部通过,P0 回归测试无退化
2026-07-07 12:56:52 +08:00

71 lines
2.0 KiB
Python

"""长文本分段工具 — P1 长文本分段合成。
将超过阈值的文本按句子边界分段,供 CosyVoice 并发合成后合并。
"""
from __future__ import annotations
# 中文句子结束符(含全角/半角)
_SENTENCE_ENDS = frozenset("。!?;\n.!?;")
def split_text(text: str, max_chars: int = 500) -> list[str]:
"""将文本分段,每段不超过 max_chars 个字符。
优先在句子边界(句号、问号、感叹号、换行符)处分段。
若单个句子超过 max_chars,则在逗号等次级标点处拆分。
若仍超长,则硬切。
Args:
text: 待分段文本
max_chars: 每段最大字符数
Returns:
分段列表,每段 ≤ max_chars。文本为空时返回空列表。
"""
text = text.strip()
if not text:
return []
if len(text) <= max_chars:
return [text]
segments: list[str] = []
current = ""
for char in text:
current += char
if char in _SENTENCE_ENDS and len(current) >= 50:
# 句子边界且长度合理,切段
segments.append(current.strip())
current = ""
elif len(current) >= max_chars:
# 达到上限,强制切段
segments.append(current.strip())
current = ""
if current.strip():
segments.append(current.strip())
# 合并过短的段(< 50 字符且不是最后一段),减少 API 调用次数
merged: list[str] = []
buffer = ""
for seg in segments:
if buffer:
combined = buffer + seg
if len(combined) <= max_chars:
buffer = combined
continue
merged.append(buffer)
buffer = ""
if len(seg) < 50:
buffer = seg
else:
merged.append(seg)
if buffer:
if merged and len(merged[-1]) + len(buffer) <= max_chars:
merged[-1] = merged[-1] + buffer
else:
merged.append(buffer)
return [s for s in merged if s]