import asyncio from typing import Dict, List from config import settings from utils.logger import session_logger from services.audio_session import AudioSession from services.conversation_history import conversation_history_manager from services.interrupt_handler import interrupt_handler from services.task_manager import task_manager from services.text_generator import TextGenerator from services.tts_synthesizer import TTSSynthesizer from services.audio_sender import AudioSender from services.interruption_helper import InterruptionHelper import re async def _cleanup_queues(text_queue: asyncio.Queue, url_queue: asyncio.Queue, device_id: str, session_id: str): """清理响应处理中使用的队列""" try: # 清理text_queue text_count = 0 while not text_queue.empty(): try: item = text_queue.get_nowait() text_queue.task_done() text_count += 1 del item except asyncio.QueueEmpty: break # 清理url_queue url_count = 0 while not url_queue.empty(): try: item = url_queue.get_nowait() url_queue.task_done() url_count += 1 del item except asyncio.QueueEmpty: break session_logger.info( device_id, session_id, f"队列清理完成 - text_queue: {text_count}项, url_queue: {url_count}项" ) except Exception as e: session_logger.error( device_id, session_id, f"队列清理时出错: {e}" ) SENSITIVE_RESPONSES = [ "你好,我无法给到相关内容", "我无法给到相关内容", "Sorry, I didn't catch that", "I didn't catch that", "让我们换一个话题", "Let's change the topic", "Changeons de sujet", "Lassen Sie uns das Thema wechseln", "Cambiemos de tema", "Mari kita tukar topik" ] def is_sensitive_response(reply: str) -> bool: """ 检查回复是否为敏感话题回复,考虑不同语言的标点符号差异 """ def normalize_text(text): text = text.lower() punctuations = r'[!"#$%&\'()*+,-./:;<=>?@\[\\\]^_`{|}~""''、。,!?:;()【】「」『』〈〉《》〔〕…—¥€£¥]' text = re.sub(punctuations, '', text) text = re.sub(r'\s+', '', text) full_to_half = str.maketrans({ '0': '0', '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9', 'A': 'a', 'B': 'b', 'C': 'c', 'D': 'd', 'E': 'e', 'F': 'f', 'G': 'g', 'H': 'h', 'I': 'i', 'J': 'j', 'K': 'k', 'L': 'l', 'M': 'm', 'N': 'n', 'O': 'o', 'P': 'p', 'Q': 'q', 'R': 'r', 'S': 's', 'T': 't', 'U': 'u', 'V': 'v', 'W': 'w', 'X': 'x', 'Y': 'y', 'Z': 'z', 'a': 'a', 'b': 'b', 'c': 'c', 'd': 'd', 'e': 'e', 'f': 'f', 'g': 'g', 'h': 'h', 'i': 'i', 'j': 'j', 'k': 'k', 'l': 'l', 'm': 'm', 'n': 'n', 'o': 'o', 'p': 'p', 'q': 'q', 'r': 'r', 's': 's', 't': 't', 'u': 'u', 'v': 'v', 'w': 'w', 'x': 'x', 'y': 'y', 'z': 'z', ' ': '' }) text = text.translate(full_to_half) return text normalized_reply = normalize_text(reply) for sensitive_reply in SENSITIVE_RESPONSES: normalized_sensitive = normalize_text(sensitive_reply) if normalized_sensitive in normalized_reply: return True return False async def process_response( transcript: str, history: List[Dict[str, str]], selected_role: dict, device_id: str, session_id: str, device_history, session: AudioSession, language: str = None, ): text_queue = asyncio.Queue() # 从LLM到TTS传递文本 url_queue = asyncio.Queue() # 从TTS到发送任务传递音频URL session_key = (device_id, session_id) await InterruptionHelper.register_queue_cleanup(session_key, url_queue) await InterruptionHelper.register_queue_cleanup(session_key, text_queue) text_generator = TextGenerator(device_id, session_id) tts_synthesizer = TTSSynthesizer(device_id, session_id) audio_sender = AudioSender(device_id, session_id, session.websocket) llm_service = session.llm_service tts_service = session.tts_service if isinstance(tts_service.__class__.__name__, str) and tts_service.__class__.__name__ == "AliyunTTS": tts_service = await get_tts(selected_role, language) session.tts_service = tts_service session_logger.warning( device_id, session_id, "检测到AliyunTTS已不再支持,自动切换到MiniMaxTTS" ) text_gen_task = await task_manager.create_task( text_generator.generate_text( llm_service, transcript, history, selected_role, text_queue ), device_id=device_id, session_key=(device_id, session_id), task_type="text_generation" ) tts_task = await task_manager.create_task( tts_synthesizer.synthesize_audio( tts_service, text_queue, url_queue, selected_role, language # 传递语言参数到TTS ), device_id=device_id, session_key=(device_id, session_id), task_type="tts_synthesis" ) sender_task = await task_manager.create_task( audio_sender.send_audio_urls(url_queue), device_id=device_id, session_key=(device_id, session_id), task_type="audio_sender" ) reply = "" try: reply = await text_gen_task await asyncio.gather(tts_task, sender_task) except Exception as e: session_logger.error( device_id, session_id, f"响应处理过程中出错: {e}", exc_info=True ) finally: # 清理队列 await _cleanup_queues(text_queue, url_queue, device_id, session_id) if not interrupt_handler.is_interrupted(session_key): if is_sensitive_response(reply): session_logger.info( device_id, session_id, "检测到敏感话题回复,该轮对话将不被添加到历史记录" ) else: device_history.history.append({"user": transcript, "assistant": reply}) if len(device_history.history) > settings.max_conversation_history: device_history.history = device_history.history[-settings.max_conversation_history:] role_key = selected_role.get("role_key", settings.selected_role_key) device_history.role_key = role_key await conversation_history_manager.set_history(device_id, device_history, role_key) session_logger.info( device_id, session_id, f"已将本轮对话添加到角色 {role_key} 的历史记录并保存到数据库" ) return reply