add banbanmini backend

This commit is contained in:
HycJack
2026-03-24 15:04:36 +08:00
parent 0d0f995dc2
commit 7510ca6df1
197 changed files with 13008 additions and 0 deletions

View File

@@ -0,0 +1,184 @@
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', '': '1', '': '2', '': '3', '': '4',
'': '5', '': '6', '': '7', '': '8', '': '9',
'': 'a', '': 'b', '': 'c', '': 'd', '': 'e',
'': 'f', '': 'g', '': 'h', '': 'i', '': 'j',
'': 'k', '': 'l', '': 'm', '': 'n', '': 'o',
'': 'p', '': 'q', '': 'r', '': 's', '': 't',
'': 'u', '': 'v', '': 'w', '': 'x', '': 'y',
'': 'z', '': 'a', '': 'b', '': 'c', '': 'd',
'': 'e', '': 'f', '': 'g', '': 'h', '': 'i',
'': 'j', '': 'k', '': 'l', '': 'm', '': 'n',
'': 'o', '': 'p', '': 'q', '': 'r', '': 's',
'': 't', '': 'u', '': 'v', '': 'w', '': 'x',
'': 'y', '': '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