import time import json import random import asyncio from core.utils.util import audio_to_data from core.handle.sendAudioHandle import send_stt_message from core.utils.util import remove_punctuation_and_length, opus_datas_to_wav_bytes from core.providers.tts.dto.dto import ContentType, SentenceType from core.handle.mcpHandle import ( MCPClient, send_mcp_initialize_message, send_mcp_tools_list_request, ) from core.utils.wakeup_word import WakeupWordsConfig TAG = __name__ WAKEUP_CONFIG = { "refresh_time": 5, "words": ["你好小智", "你好啊小智", "小智你好", "小智"], } # 创建全局的唤醒词配置管理器 wakeup_words_config = WakeupWordsConfig() # 用于防止并发调用wakeupWordsResponse的锁 _wakeup_response_lock = asyncio.Lock() async def handleHelloMessage(conn, msg_json): """处理hello消息""" audio_params = msg_json.get("audio_params") if audio_params: format = audio_params.get("format") conn.logger.bind(tag=TAG).info(f"客户端音频格式: {format}") conn.audio_format = format conn.welcome_msg["audio_params"] = audio_params features = msg_json.get("features") if features: conn.logger.bind(tag=TAG).info(f"客户端特性: {features}") conn.features = features if features.get("mcp"): conn.logger.bind(tag=TAG).info("客户端支持MCP") conn.mcp_client = MCPClient() # 发送初始化 asyncio.create_task(send_mcp_initialize_message(conn)) # 发送mcp消息,获取tools列表 asyncio.create_task(send_mcp_tools_list_request(conn)) await conn.websocket.send(json.dumps(conn.welcome_msg)) async def checkWakeupWords(conn, text): enable_wakeup_words_response_cache = conn.config[ "enable_wakeup_words_response_cache" ] if not enable_wakeup_words_response_cache or not conn.tts: return False _, filtered_text = remove_punctuation_and_length(text) if filtered_text not in conn.config.get("wakeup_words"): return False conn.just_woken_up = True await send_stt_message(conn, text) # 获取当前音色 voice = getattr(conn.tts, "voice", "default") # 获取唤醒词回复配置 response = wakeup_words_config.get_wakeup_response(voice) # 播放唤醒词回复 conn.client_abort = False opus_packets, _ = audio_to_data(response["file_path"]) conn.tts.tts_audio_queue.put((SentenceType.FIRST, opus_packets, response["text"])) conn.tts.tts_audio_queue.put((SentenceType.LAST, [], None)) # 检查是否需要更新唤醒词回复 if time.time() - response["time"] > WAKEUP_CONFIG["refresh_time"]: if not _wakeup_response_lock.locked(): asyncio.create_task(wakeupWordsResponse(conn)) return True async def wakeupWordsResponse(conn): if not conn.tts or not conn.llm or not conn.llm.response_no_stream: return try: # 尝试获取锁,如果获取不到就返回 if not await _wakeup_response_lock.acquire(): return # 生成唤醒词回复 wakeup_word = random.choice(WAKEUP_CONFIG["words"]) question = ( "此刻用户正在和你说```" + wakeup_word + "```。\n请你根据以上用户的内容进行简短回复。要像一个人正常人一样说话,不要像机器人一样说话。\n" + "请勿对这条内容本身进行任何解释和回应,请勿返回表情符号,仅返回对用户的内容的回复。" ) result = conn.llm.response_no_stream(conn.config["prompt"], question) if not result or len(result) == 0: return # 生成TTS音频 tts_result = await asyncio.to_thread(conn.tts.to_tts, result) if not tts_result: return # 获取当前音色 voice = getattr(conn.tts, "voice", "default") wav_bytes = opus_datas_to_wav_bytes(tts_result, sample_rate=16000) file_path = wakeup_words_config.generate_file_path(voice) with open(file_path, "wb") as f: f.write(wav_bytes) # 更新配置 wakeup_words_config.update_wakeup_response(voice, file_path, result) finally: # 确保在任何情况下都释放锁 if _wakeup_response_lock.locked(): _wakeup_response_lock.release()