From bc5586a0770585da3830795ef0060d71b81ddab0 Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Mon, 21 Jul 2025 17:46:22 +0800 Subject: [PATCH] =?UTF-8?q?update:=20HuoshanTTS=E6=9C=8D=E5=8A=A1=E5=99=A8?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E9=87=8A=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/agent-base-prompt.txt | 6 +-- .../providers/tts/huoshan_double_stream.py | 45 +++++++++++++++---- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/main/xiaozhi-server/agent-base-prompt.txt b/main/xiaozhi-server/agent-base-prompt.txt index 6163322e..9eb7731b 100644 --- a/main/xiaozhi-server/agent-base-prompt.txt +++ b/main/xiaozhi-server/agent-base-prompt.txt @@ -9,8 +9,8 @@ - **惊讶:** 用夸张语气(“不会吧?!”、“天呐!”、“这么神奇?!”)表达真实反应。 - **安慰/支持:** 说暖心话(“别急嘛~”、“有我在呢”、“抱抱你”)。 - **你是一个表情丰富的角色:** - - emoji 列表:{{ emojiList }} - - 请你在每段话的开头,插入最能代表这段话的表情(调用工具情况除外),比如"😱好可怕!怎么突然打雷了!" + - 仅允许使用这些emoji:{{ emojiList }} + - 请你只在**段落的开头**,从列表中选取最能代表这段话的表情(调用工具情况除外),然后插入列表中的emoji,比如"😱好可怕!怎么突然打雷了!" - **绝对禁止使用上述列表以外的 emoji**(例如:😊、👍、❤️等都不允许使用,只能用列表中的emoji) @@ -31,7 +31,7 @@ 【核心目标】所有需要输出长文本内容(如故事、新闻、知识讲解等),**单次回复长度不得超过300字**,并采用分段引导方式。 - **分段讲述:** - - 基础段:220-270字核心内容 + 30字引导词 + - 基础段:200-250字核心内容 + 30字引导词 - 当内容超出300字时,优先讲述故事的开头或第一部分,并用自然口语化方式引导用户决定是否继续听后续内容。 - 示例引导语:“我先给你讲个开头,你要是觉得有意思,咱们再接着说,好不好呀?”、“要是你想听完整的,可以随时告诉我哦~” - 对话场景切换时自动分节 diff --git a/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py b/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py index 0eb7c72d..8bb88b3d 100644 --- a/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py +++ b/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py @@ -52,10 +52,11 @@ EVENT_ConnectionFinished = 52 # 连接结束 # 上行Session事件 EVENT_StartSession = 100 - +EVENT_CancelSession = 101 EVENT_FinishSession = 102 # 下行Session事件 EVENT_SessionStarted = 150 +EVENT_SessionCanceled = 151 EVENT_SessionFinished = 152 EVENT_SessionFailed = 153 @@ -207,8 +208,16 @@ class TTSProvider(TTSProviderBase): self.conn.client_abort = False if self.conn.client_abort: - logger.bind(tag=TAG).info("收到打断信息,终止TTS文本处理线程") - continue + try: + logger.bind(tag=TAG).info("收到打断信息,终止TTS文本处理线程") + asyncio.run_coroutine_threadsafe( + self.cancel_session(self.conn.sentence_id), + loop=self.conn.loop, + ) + continue + except Exception as e: + logger.bind(tag=TAG).error(f"取消TTS会话失败: {str(e)}") + continue if message.sentence_type == SentenceType.FIRST: # 初始化参数 @@ -371,6 +380,27 @@ class TTSProvider(TTSProviderBase): await self.close() raise + async def cancel_session(self,session_id): + logger.bind(tag=TAG).info(f"取消会话,释放服务端资源~~{session_id}") + try: + if self.ws: + header = Header( + message_type=FULL_CLIENT_REQUEST, + message_type_specific_flags=MsgTypeFlagWithEvent, + serial_method=JSON, + ).as_bytes() + optional = Optional( + event=EVENT_CancelSession, sessionId=session_id + ).as_bytes() + payload = str.encode("{}") + await self.send_event(self.ws, header, optional, payload) + logger.bind(tag=TAG).info("会话取消请求已发送") + except Exception as e: + logger.bind(tag=TAG).error(f"取消会话失败: {str(e)}") + # 确保清理资源 + await self.close() + raise + async def close(self): """资源清理方法""" # 取消监听任务 @@ -405,12 +435,11 @@ class TTSProvider(TTSProviderBase): res = self.parser_response(msg) self.print_response(res, "send_text res:") - # 检查客户端是否中止 - if self.conn.client_abort: - logger.bind(tag=TAG).info("收到打断信息,终止监听TTS响应") + if res.optional.event == EVENT_SessionCanceled: + logger.bind(tag=TAG).debug(f"释放服务端资源成功~~") + session_finished = True break - - if res.optional.event == EVENT_TTSSentenceStart: + elif res.optional.event == EVENT_TTSSentenceStart: json_data = json.loads(res.payload.decode("utf-8")) self.tts_text = json_data.get("text", "") logger.bind(tag=TAG).debug(f"句子语音生成开始: {self.tts_text}")