Skip to content

Commit e052c6d

Browse files
authored
修复主动搭话流式输出中 [PASS] 标记未被拦截导致 TTS 朗读的问题 (#790)
* 修复主动搭话流式输出中 [PASS] 标记未被拦截导致 TTS 朗读的问题 * 引入 pass_probe 滚动尾部缓冲区(5 字符 = len("[PASS]") - 1):每次新 chunk 到达时,将 pass_probe + content 合并后检测 [PASS],解决了 '[PA' + 'SS]' 跨 chunk 拆分的问题 * cleaned 现在也经由 pass_probe 滚动缓冲区处理,尾部 5 字符保留到 pass_probe,只有确认安全的 safe_text 才会送入 TTS,消除了跨 buffer/chunk 边界的 [PASS] 拆分泄漏 * 提取了 _emit_safe 内部异步函数,统一处理 fence 计数(pipe_count >= 2)和长度限制(> 400)检查
1 parent 625c000 commit e052c6d

1 file changed

Lines changed: 50 additions & 22 deletions

File tree

main_routers/system_router.py

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3187,6 +3187,29 @@ async def _fetch_meme_with_fallback(kw: str):
31873187
full_text = ""
31883188
pipe_count = 0
31893189
aborted = False
3190+
# 滚动尾部缓冲区:保留最近 5 个字符以检测跨 chunk 的 "[PASS]"(长度 6)
3191+
pass_probe = ""
3192+
_PASS_PROBE_LEN = 5 # len("[PASS]") - 1
3193+
3194+
async def _emit_safe(text: str) -> bool:
3195+
"""通过 fence/长度检查后送入 TTS。返回 True 表示应 abort。"""
3196+
nonlocal pipe_count, full_text, aborted
3197+
if not text:
3198+
return False
3199+
for ch in text:
3200+
if ch in ('|', '|'):
3201+
pipe_count += 1
3202+
if pipe_count >= 2:
3203+
print(f"[{lanlan_name}] Phase 2 fence 触发 (pipe_count={pipe_count}),abort")
3204+
aborted = True
3205+
return True
3206+
if len(full_text) + len(text) > 400:
3207+
print(f"[{lanlan_name}] Phase 2 长度超限 ({len(full_text)+len(text)} > 400),abort")
3208+
aborted = True
3209+
return True
3210+
full_text += text
3211+
await mgr.feed_tts_chunk(text)
3212+
return False
31903213

31913214
try:
31923215
async with asyncio.timeout(25.0):
@@ -3220,38 +3243,44 @@ async def _fetch_meme_with_fallback(kw: str):
32203243
aborted = True
32213244
break
32223245

3223-
# 缓冲中剩余的文本作为首批内容
3246+
# 缓冲中剩余的文本经由 pass_probe 逻辑输出
32243247
if cleaned.strip():
3225-
full_text += cleaned
3226-
await mgr.feed_tts_chunk(cleaned)
3248+
combined = pass_probe + cleaned
3249+
if '[PASS]' in combined.upper():
3250+
print(f"[{lanlan_name}] Phase 2 流式检测到 [PASS],abort")
3251+
aborted = True
3252+
break
3253+
safe_text = combined[:-_PASS_PROBE_LEN] if len(combined) > _PASS_PROBE_LEN else ''
3254+
pass_probe = combined[-_PASS_PROBE_LEN:] if len(combined) >= _PASS_PROBE_LEN else combined
3255+
if await _emit_safe(safe_text):
3256+
break
32273257
continue
32283258

3229-
# --- 在线拦截: fence ---
3230-
fence_hit = False
3231-
for ch in content:
3232-
if ch in ('|', '|'):
3233-
pipe_count += 1
3234-
if pipe_count >= 2:
3235-
fence_hit = True
3236-
break
3237-
if fence_hit:
3238-
print(f"[{lanlan_name}] Phase 2 流式 fence 触发 (pipe_count={pipe_count}),abort")
3259+
# --- 在线拦截: [PASS](含跨 chunk 检测)---
3260+
combined = pass_probe + content
3261+
if '[PASS]' in combined.upper():
3262+
print(f"[{lanlan_name}] Phase 2 流式检测到内嵌 [PASS],abort")
32393263
aborted = True
32403264
break
3265+
# 将本次 chunk 的尾部保留到 pass_probe,可安全输出的部分为去掉尾部的前段
3266+
safe_text = combined[:-_PASS_PROBE_LEN] if len(combined) > _PASS_PROBE_LEN else ''
3267+
pass_probe = combined[-_PASS_PROBE_LEN:] if len(combined) >= _PASS_PROBE_LEN else combined
32413268

3242-
# --- 在线拦截: 长度 ---
3243-
if len(full_text) + len(content) > 400:
3244-
print(f"[{lanlan_name}] Phase 2 流式长度超限 ({len(full_text)+len(content)} > 400),abort")
3245-
aborted = True
3269+
if safe_text and await _emit_safe(safe_text):
32463270
break
3247-
3248-
full_text += content
3249-
await mgr.feed_tts_chunk(content)
32503271

32513272
except (asyncio.TimeoutError, Exception) as e:
32523273
logger.warning(f"[{lanlan_name}] Phase 2 流式调用异常: {type(e).__name__}: {e}")
32533274
aborted = True
32543275

3276+
# --- 流结束后:flush pass_probe 残留 ---
3277+
if pass_probe and not aborted:
3278+
if '[PASS]' in pass_probe.upper():
3279+
aborted = True
3280+
else:
3281+
await _emit_safe(pass_probe)
3282+
pass_probe = ""
3283+
32553284
# --- 流结束后 buffer 未 flush 的兜底处理 ---
32563285
if not tag_parsed and buffer and not aborted:
32573286
cleaned = buffer
@@ -3265,8 +3294,7 @@ async def _fetch_meme_with_fallback(kw: str):
32653294
if source_tag == 'PASS' or '[PASS]' in cleaned.upper():
32663295
aborted = True
32673296
elif cleaned.strip():
3268-
full_text += cleaned
3269-
await mgr.feed_tts_chunk(cleaned)
3297+
await _emit_safe(cleaned)
32703298

32713299
# --- 结果处理 ---
32723300
print(f"\n[PROACTIVE-DEBUG] Phase 2 STREAM output (aborted={aborted}, tag={source_tag}): {(buffer + full_text)[:300]}\n")

0 commit comments

Comments
 (0)