diff --git a/agent/canvas.py b/agent/canvas.py index 87de8a5394..b19b4cbb8f 100644 --- a/agent/canvas.py +++ b/agent/canvas.py @@ -835,6 +835,8 @@ class Canvas(Graph): ) text = emoji_pattern.sub("", text) + text = re.sub(r"<[^>]*>", "", text) + text = re.sub(r"\s+", " ", text).strip() MAX_LEN = 500 diff --git a/api/db/services/dialog_service.py b/api/db/services/dialog_service.py index 2412c05d4f..3a90dc29c4 100644 --- a/api/db/services/dialog_service.py +++ b/api/db/services/dialog_service.py @@ -14,6 +14,7 @@ # limitations under the License. # import asyncio +import html import logging import re import time @@ -807,8 +808,15 @@ async def async_chat(dialog, messages, stream=True, **kwargs): retrieval_ts = timer() if not knowledges and prompt_config.get("empty_response"): empty_res = prompt_config["empty_response"] - yield {"answer": empty_res, "reference": {}, "prompt": "", "audio_binary": None, "final": False} - yield {"answer": empty_res, "reference": kbinfos, "prompt": "\n\n### Query:\n%s" % " ".join(questions), "audio_binary": tts(tts_mdl, empty_res), "final": True} + logging.debug("async_chat empty_response path: empty_res=%r tts_mdl=%r", empty_res, tts_mdl) + # HTML-escape for frontend display so DOMPurify does not strip + # unknown tags (e.g. → <abc>), which would otherwise + # leave the content blank and stall the UI on "Searching…". + # The raw value is still used for TTS (which has its own tag- + # stripping in clean_tts_text). + escaped_answer = html.escape(empty_res) + yield {"answer": escaped_answer, "reference": {}, "prompt": "", "audio_binary": None, "final": False} + yield {"answer": escaped_answer, "reference": kbinfos, "prompt": "\n\n### Query:\n%s" % " ".join(questions), "audio_binary": tts(tts_mdl, empty_res), "final": True} return # Only overwrite kwargs["knowledge"] when retrieval produced something; @@ -1481,6 +1489,8 @@ def clean_tts_text(text: str) -> str: if not text: return "" + logging.debug("clean_tts_text BEFORE: %r", text) + text = text.encode("utf-8", "ignore").decode("utf-8", "ignore") text = re.sub(r"[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]", "", text) @@ -1490,12 +1500,17 @@ def clean_tts_text(text: str) -> str: ) text = emoji_pattern.sub("", text) + # Strip XML/SSML/HTML-like tags so the TTS engine does not hang on + # unclosed or unknown markup (e.g. in empty_response). + text = re.sub(r"<[^>]*>", "", text) + text = re.sub(r"\s+", " ", text).strip() MAX_LEN = 500 if len(text) > MAX_LEN: text = text[:MAX_LEN] + logging.debug("clean_tts_text AFTER: %r", text) return text