|
|
import time
|
|
|
import json
|
|
|
import random
|
|
|
import asyncio
|
|
|
from core.utils.dialogue import Message
|
|
|
from core.utils.util import audio_to_data
|
|
|
from core.handle.sendAudioHandle import sendAudioMessage, 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.providers.tools.device_mcp 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")
|
|
|
if not voice:
|
|
|
voice = "default"
|
|
|
|
|
|
# 获取唤醒词回复配置
|
|
|
response = wakeup_words_config.get_wakeup_response(voice)
|
|
|
if not response or not response.get("file_path"):
|
|
|
response = {
|
|
|
"voice": "default",
|
|
|
"file_path": "config/assets/wakeup_words.wav",
|
|
|
"time": 0,
|
|
|
"text": "哈啰啊,我是小智啦,声音好听的台湾女孩一枚,超开心认识你耶,最近在忙啥,别忘了给我来点有趣的料哦,我超爱听八卦的啦",
|
|
|
}
|
|
|
|
|
|
# 播放唤醒词回复
|
|
|
conn.client_abort = False
|
|
|
opus_packets, _ = audio_to_data(response.get("file_path"))
|
|
|
|
|
|
conn.logger.bind(tag=TAG).info(f"播放唤醒词回复: {response.get('text')}")
|
|
|
await sendAudioMessage(conn, SentenceType.FIRST, opus_packets, response.get("text"))
|
|
|
await sendAudioMessage(conn, SentenceType.LAST, [], None)
|
|
|
|
|
|
# 补充对话
|
|
|
conn.dialogue.put(Message(role="assistant", content=response.get("text")))
|
|
|
|
|
|
# 检查是否需要更新唤醒词回复
|
|
|
if time.time() - response.get("time", 0) > 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请你根据以上用户的内容进行20-30字回复。要符合系统设置的角色情感和态度,不要像机器人一样说话。\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()
|