@@ -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