diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index fc674527..9d1949c4 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -110,10 +110,8 @@ class ConnectionHandler: # vad相关变量 self.client_audio_buffer = bytearray() self.client_have_voice = False - self.client_have_voice_last_time = 0.0 - self.client_no_voice_last_time = 0.0 + self.last_activity_time = 0.0 # 统一的活动时间戳(毫秒) self.client_voice_stop = False - self.client_voice_frame_count = 0 # asr相关变量 # 因为实际部署时可能会用到公共的本地ASR,不能把变量暴露给公共ASR @@ -143,10 +141,10 @@ class ConnectionHandler: self.load_function_plugin = False self.intent_type = "nointent" - self.timeout_task = None self.timeout_seconds = ( int(self.config.get("close_connection_no_voice_time", 120)) + 60 ) # 在原来第一道关闭的基础上加60秒,进行二道关闭 + self.timeout_task = None # {"mcp":true} 表示启用MCP功能 self.features = None @@ -187,6 +185,9 @@ class ConnectionHandler: self.websocket = ws self.device_id = self.headers.get("device-id", None) + # 初始化活动时间戳 + self.last_activity_time = time.time() * 1000 + # 启动超时检查任务 self.timeout_task = asyncio.create_task(self._check_timeout()) @@ -243,12 +244,8 @@ class ConnectionHandler: async def _route_message(self, message): """消息路由""" - # 重置超时计时器 - if self.timeout_task: - self.timeout_task.cancel() - self.timeout_task = asyncio.create_task(self._check_timeout()) - if isinstance(message, str): + self.last_activity_time = time.time() * 1000 await handleTextMessage(self, message) elif isinstance(message, bytes): if self.vad is None: @@ -898,7 +895,6 @@ class ConnectionHandler: def reset_vad_states(self): self.client_audio_buffer = bytearray() self.client_have_voice = False - self.client_have_voice_last_time = 0 self.client_voice_stop = False self.logger.bind(tag=TAG).debug("VAD states reset.") @@ -917,10 +913,18 @@ class ConnectionHandler: """检查连接超时""" try: while not self.stop_event.is_set(): - await asyncio.sleep(self.timeout_seconds) - if not self.stop_event.is_set(): - self.logger.bind(tag=TAG).info("连接超时,准备关闭") - await self.close(self.websocket) - break + # 检查是否超时(只有在时间戳已初始化的情况下) + if self.last_activity_time > 0.0: + current_time = time.time() * 1000 + if ( + current_time - self.last_activity_time + > self.timeout_seconds * 1000 + ): + if not self.stop_event.is_set(): + self.logger.bind(tag=TAG).info("连接超时,准备关闭") + await self.close(self.websocket) + break + # 每10秒检查一次,避免过于频繁 + await asyncio.sleep(10) except Exception as e: self.logger.bind(tag=TAG).error(f"超时检查任务出错: {e}") diff --git a/main/xiaozhi-server/core/handle/receiveAudioHandle.py b/main/xiaozhi-server/core/handle/receiveAudioHandle.py index fd58f49d..465b081a 100644 --- a/main/xiaozhi-server/core/handle/receiveAudioHandle.py +++ b/main/xiaozhi-server/core/handle/receiveAudioHandle.py @@ -66,12 +66,11 @@ async def startToChat(conn, text): async def no_voice_close_connect(conn, have_voice): if have_voice: - conn.client_no_voice_last_time = 0.0 + conn.last_activity_time = time.time() * 1000 return - if conn.client_no_voice_last_time == 0.0: - conn.client_no_voice_last_time = time.time() * 1000 - else: - no_voice_time = time.time() * 1000 - conn.client_no_voice_last_time + # 只有在已经初始化过时间戳的情况下才进行超时检查 + if conn.last_activity_time > 0.0: + no_voice_time = time.time() * 1000 - conn.last_activity_time close_connection_no_voice_time = int( conn.config.get("close_connection_no_voice_time", 120) ) diff --git a/main/xiaozhi-server/core/handle/sendAudioHandle.py b/main/xiaozhi-server/core/handle/sendAudioHandle.py index 6e3bca62..eb9f565f 100644 --- a/main/xiaozhi-server/core/handle/sendAudioHandle.py +++ b/main/xiaozhi-server/core/handle/sendAudioHandle.py @@ -93,7 +93,7 @@ async def sendAudio(conn, audios, pre_buffer=True): break # 重置没有声音的状态 - conn.client_no_voice_last_time = 0.0 + conn.last_activity_time = time.time() * 1000 # 计算预期发送时间 expected_time = start_time + (play_position / 1000) diff --git a/main/xiaozhi-server/core/providers/vad/silero.py b/main/xiaozhi-server/core/providers/vad/silero.py index 9df5188c..2c45ba8c 100644 --- a/main/xiaozhi-server/core/providers/vad/silero.py +++ b/main/xiaozhi-server/core/providers/vad/silero.py @@ -35,7 +35,7 @@ class VADProvider(VADProviderBase): pcm_frame = self.decoder.decode(opus_packet, 960) conn.client_audio_buffer.extend(pcm_frame) # 将新数据加入缓冲区 - # 初始化帧计数器 + # 确保帧计数器存在 if not hasattr(conn, "client_voice_frame_count"): conn.client_voice_frame_count = 0 @@ -66,14 +66,12 @@ class VADProvider(VADProviderBase): # 如果之前有声音,但本次没有声音,且与上次有声音的时间差已经超过了静默阈值,则认为已经说完一句话 if conn.client_have_voice and not client_have_voice: - stop_duration = ( - time.time() * 1000 - conn.client_have_voice_last_time - ) + stop_duration = time.time() * 1000 - conn.last_activity_time if stop_duration >= self.silence_threshold_ms: conn.client_voice_stop = True if client_have_voice: conn.client_have_voice = True - conn.client_have_voice_last_time = time.time() * 1000 + conn.last_activity_time = time.time() * 1000 return client_have_voice except opuslib_next.OpusError as e: