import json import asyncio from core.providers.tts.dto.dto import SentenceType from core.utils import textUtils TAG = __name__ async def sendAudioMessage(conn, sentenceType, audios, text): if conn.tts.tts_audio_first_sentence: conn.logger.bind(tag=TAG).info(f"发送第一段语音: {text}") conn.tts.tts_audio_first_sentence = False await send_tts_message(conn, "start", None) if sentenceType == SentenceType.FIRST: await send_tts_message(conn, "sentence_start", text) await sendAudio(conn, audios) # 发送句子开始消息 if sentenceType is not SentenceType.MIDDLE: conn.logger.bind(tag=TAG).info(f"发送音频消息: {sentenceType}, {text}") # 发送结束消息(如果是最后一个文本) if conn.llm_finish_task and sentenceType == SentenceType.LAST: await send_tts_message(conn, "stop", None) conn.client_is_speaking = False if conn.close_after_chat: await conn.close() # 播放音频 async def sendAudio(conn, audios): if audios is None: return # 如果audios不是opus数组,则不需要进行遍历,可以直接发送;这里需要进行流控管理,防止发送过快引发客户端溢出 if isinstance(audios, bytes): await conn.websocket.send(audios) async def send_tts_message(conn, state, text=None): """发送 TTS 状态消息""" if text is None and state == "sentence_start": return message = {"type": "tts", "state": state, "session_id": conn.session_id} if text is not None: message["text"] = textUtils.check_emoji(text) # TTS播放结束 if state == "stop": # 播放提示音 tts_notify = conn.config.get("enable_stop_tts_notify", False) if tts_notify: stop_tts_notify_voice = conn.config.get( "stop_tts_notify_voice", "config/assets/tts_notify.mp3" ) conn.tts.audio_to_opus_data_stream( stop_tts_notify_voice, callback=lambda audio_data: asyncio.run_coroutine_threadsafe( sendAudio(conn, audio_data), conn.loop ), ) # 清除服务端讲话状态 conn.clearSpeakStatus() # 发送消息到客户端 await conn.websocket.send(json.dumps(message)) async def send_stt_message(conn, text): """发送 STT 状态消息""" end_prompt_str = conn.config.get("end_prompt", {}).get("prompt") if end_prompt_str and end_prompt_str == text: await send_tts_message(conn, "start") return # 解析JSON格式,提取实际的用户说话内容 display_text = text try: # 尝试解析JSON格式 if text.strip().startswith('{') and text.strip().endswith('}'): parsed_data = json.loads(text) if isinstance(parsed_data, dict) and "content" in parsed_data: # 如果是包含说话人信息的JSON格式,只显示content部分 display_text = parsed_data["content"] # 保存说话人信息到conn对象 if "speaker" in parsed_data: conn.current_speaker = parsed_data["speaker"] except (json.JSONDecodeError, TypeError): # 如果不是JSON格式,直接使用原始文本 display_text = text stt_text = textUtils.get_string_no_punctuation_or_emoji(display_text) await conn.websocket.send( json.dumps({"type": "stt", "text": stt_text, "session_id": conn.session_id}) ) conn.client_is_speaking = True await send_tts_message(conn, "start")