update:优化日志对象

This commit is contained in:
hrz
2025-05-07 18:06:13 +08:00
parent a26bee3696
commit ea5f54e421
16 changed files with 249 additions and 243 deletions
+54 -70
View File
@@ -18,6 +18,8 @@ from core.utils.util import (
get_string_no_punctuation_or_emoji, get_string_no_punctuation_or_emoji,
extract_json_from_string, extract_json_from_string,
initialize_modules, initialize_modules,
check_vad_update,
check_asr_update,
) )
from concurrent.futures import ThreadPoolExecutor, TimeoutError from concurrent.futures import ThreadPoolExecutor, TimeoutError
from core.handle.sendAudioHandle import sendAudioMessage from core.handle.sendAudioHandle import sendAudioMessage
@@ -52,7 +54,9 @@ class ConnectionHandler:
_intent, _intent,
server=None, server=None,
): ):
self.config = config self.common_config = config
self.config = copy.deepcopy(config)
self.session_id = str(uuid.uuid4())
self.logger = setup_logging() self.logger = setup_logging()
self.auth = AuthMiddleware(config) self.auth = AuthMiddleware(config)
self.server = server # 保存server实例的引用 self.server = server # 保存server实例的引用
@@ -66,7 +70,6 @@ class ConnectionHandler:
self.device_id = None self.device_id = None
self.client_ip = None self.client_ip = None
self.client_ip_info = {} self.client_ip_info = {}
self.session_id = None
self.prompt = None self.prompt = None
self.welcome_msg = None self.welcome_msg = None
self.max_output_size = 0 self.max_output_size = 0
@@ -87,8 +90,10 @@ class ConnectionHandler:
self.tts_report_thread = None self.tts_report_thread = None
# 依赖的组件 # 依赖的组件
self.vad = _vad self.vad = None
self.asr = _asr self.asr = None
self._asr = _asr
self._vad = _vad
self.llm = _llm self.llm = _llm
self.tts = _tts self.tts = _tts
self.memory = _memory self.memory = _memory
@@ -166,7 +171,6 @@ class ConnectionHandler:
# 认证通过,继续处理 # 认证通过,继续处理
self.websocket = ws self.websocket = ws
self.device_id = self.headers.get("device-id", None) self.device_id = self.headers.get("device-id", None)
self.session_id = str(uuid.uuid4())
# 启动超时检查任务 # 启动超时检查任务
self.timeout_task = asyncio.create_task(self._check_timeout()) self.timeout_task = asyncio.create_task(self._check_timeout())
@@ -176,9 +180,9 @@ class ConnectionHandler:
await self.websocket.send(json.dumps(self.welcome_msg)) await self.websocket.send(json.dumps(self.welcome_msg))
# 获取差异化配置 # 获取差异化配置
private_config = self._initialize_private_config() self._initialize_private_config()
# 异步初始化 # 异步初始化
self.executor.submit(self._initialize_components, private_config) self.executor.submit(self._initialize_components)
# tts 消化线程 # tts 消化线程
self.tts_priority_thread = threading.Thread( self.tts_priority_thread = threading.Thread(
target=self._tts_priority_thread, daemon=True target=self._tts_priority_thread, daemon=True
@@ -233,13 +237,17 @@ class ConnectionHandler:
elif isinstance(message, bytes): elif isinstance(message, bytes):
await handleAudioMessage(self, message) await handleAudioMessage(self, message)
def _initialize_components(self, private_config): def _initialize_components(self):
"""初始化组件""" """初始化组件"""
if private_config is not None: self.prompt = self.config["prompt"]
self._initialize_models(private_config) self.change_system_prompt(self.prompt)
else: self.logger.bind(tag=TAG).info(f"初始化组件: prompt成功 {self.prompt[:50]}...")
self.prompt = self.config["prompt"]
self.change_system_prompt(self.prompt) """初始化本地组件"""
if self.vad is None:
self.vad = self._vad
if self.asr is None:
self.asr = self._asr
"""加载记忆""" """加载记忆"""
self._initialize_memory() self._initialize_memory()
"""加载意图识别""" """加载意图识别"""
@@ -286,54 +294,21 @@ class ConnectionHandler:
self.logger.bind(tag=TAG).error(f"获取差异化配置失败: {e}") self.logger.bind(tag=TAG).error(f"获取差异化配置失败: {e}")
private_config = {} private_config = {}
init_tts = False init_llm, init_tts, init_memory, init_intent = (
if private_config.get("TTS", None) is not None:
init_tts = True
self.config["TTS"] = private_config["TTS"]
self.config["selected_module"]["TTS"] = private_config["selected_module"][
"TTS"
]
try:
modules = initialize_modules(
self.logger,
private_config,
False,
False,
False,
init_tts,
False,
False,
)
except Exception as e:
self.logger.bind(tag=TAG).error(f"初始化组件失败: {e}")
modules = {}
if modules.get("tts", None) is not None:
self.tts = modules["tts"]
if modules.get("prompt", None) is not None:
self.change_system_prompt(modules["prompt"])
private_config["prompt"] = None
return private_config
def _initialize_models(self, private_config):
init_vad, init_asr, init_llm, init_memory, init_intent = (
False,
False, False,
False, False,
False, False,
False, False,
) )
if private_config.get("VAD", None) is not None:
init_vad = True init_vad = check_vad_update(self.common_config, private_config)
self.config["VAD"] = private_config["VAD"] init_asr = check_asr_update(self.common_config, private_config)
self.config["selected_module"]["VAD"] = private_config["selected_module"][
"VAD" if private_config.get("TTS", None) is not None:
] init_tts = True
if private_config.get("ASR", None) is not None: self.config["TTS"] = private_config["TTS"]
init_asr = True self.config["selected_module"]["TTS"] = private_config["selected_module"][
self.config["ASR"] = private_config["ASR"] "TTS"
self.config["selected_module"]["ASR"] = private_config["selected_module"][
"ASR"
] ]
if private_config.get("LLM", None) is not None: if private_config.get("LLM", None) is not None:
init_llm = True init_llm = True
@@ -353,8 +328,11 @@ class ConnectionHandler:
self.config["selected_module"]["Intent"] = private_config[ self.config["selected_module"]["Intent"] = private_config[
"selected_module" "selected_module"
]["Intent"] ]["Intent"]
if private_config.get("prompt", None) is not None:
self.config["prompt"] = private_config["prompt"]
if private_config.get("device_max_output_size", None) is not None: if private_config.get("device_max_output_size", None) is not None:
self.max_output_size = int(private_config["device_max_output_size"]) self.max_output_size = int(private_config["device_max_output_size"])
try: try:
modules = initialize_modules( modules = initialize_modules(
self.logger, self.logger,
@@ -362,13 +340,15 @@ class ConnectionHandler:
init_vad, init_vad,
init_asr, init_asr,
init_llm, init_llm,
False, init_tts,
init_memory, init_memory,
init_intent, init_intent,
) )
except Exception as e: except Exception as e:
self.logger.bind(tag=TAG).error(f"初始化组件失败: {e}") self.logger.bind(tag=TAG).error(f"初始化组件失败: {e}")
modules = {} modules = {}
if modules.get("tts", None) is not None:
self.tts = modules["tts"]
if modules.get("vad", None) is not None: if modules.get("vad", None) is not None:
self.vad = modules["vad"] self.vad = modules["vad"]
if modules.get("asr", None) is not None: if modules.get("asr", None) is not None:
@@ -446,10 +426,12 @@ class ConnectionHandler:
processed_chars = 0 # 跟踪已处理的字符位置 processed_chars = 0 # 跟踪已处理的字符位置
try: try:
# 使用带记忆的对话 # 使用带记忆的对话
future = asyncio.run_coroutine_threadsafe( memory_str = None
self.memory.query_memory(query), self.loop if self.memory is not None:
) future = asyncio.run_coroutine_threadsafe(
memory_str = future.result() self.memory.query_memory(query), self.loop
)
memory_str = future.result()
self.logger.bind(tag=TAG).debug(f"记忆内容: {memory_str}") self.logger.bind(tag=TAG).debug(f"记忆内容: {memory_str}")
llm_responses = self.llm.response( llm_responses = self.llm.response(
@@ -510,7 +492,7 @@ class ConnectionHandler:
future = self.executor.submit( future = self.executor.submit(
self.speak_and_play, segment_text, text_index self.speak_and_play, segment_text, text_index
) )
self.tts_queue.put(future) self.tts_queue.put((future, text_index))
self.llm_finish_task = True self.llm_finish_task = True
self.dialogue.put(Message(role="assistant", content="".join(response_message))) self.dialogue.put(Message(role="assistant", content="".join(response_message)))
@@ -537,10 +519,12 @@ class ConnectionHandler:
start_time = time.time() start_time = time.time()
# 使用带记忆的对话 # 使用带记忆的对话
future = asyncio.run_coroutine_threadsafe( memory_str = None
self.memory.query_memory(query), self.loop if self.memory is not None:
) future = asyncio.run_coroutine_threadsafe(
memory_str = future.result() self.memory.query_memory(query), self.loop
)
memory_str = future.result()
# self.logger.bind(tag=TAG).info(f"对话记录: {self.dialogue.get_llm_dialogue_with_memory(memory_str)}") # self.logger.bind(tag=TAG).info(f"对话记录: {self.dialogue.get_llm_dialogue_with_memory(memory_str)}")
@@ -685,7 +669,7 @@ class ConnectionHandler:
future = self.executor.submit( future = self.executor.submit(
self.speak_and_play, segment_text, text_index self.speak_and_play, segment_text, text_index
) )
self.tts_queue.put(future) self.tts_queue.put((future, text_index))
# 存储对话内容 # 存储对话内容
if len(response_message) > 0: if len(response_message) > 0:
@@ -747,7 +731,7 @@ class ConnectionHandler:
text = result.response text = result.response
self.recode_first_last_text(text, text_index) self.recode_first_last_text(text, text_index)
future = self.executor.submit(self.speak_and_play, text, text_index) future = self.executor.submit(self.speak_and_play, text, text_index)
self.tts_queue.put(future) self.tts_queue.put((future, text_index))
self.dialogue.put(Message(role="assistant", content=text)) self.dialogue.put(Message(role="assistant", content=text))
elif result.action == Action.REQLLM: # 调用函数后再请求llm生成回复 elif result.action == Action.REQLLM: # 调用函数后再请求llm生成回复
text = result.result text = result.result
@@ -780,7 +764,7 @@ class ConnectionHandler:
text = result.result text = result.result
self.recode_first_last_text(text, text_index) self.recode_first_last_text(text, text_index)
future = self.executor.submit(self.speak_and_play, text, text_index) future = self.executor.submit(self.speak_and_play, text, text_index)
self.tts_queue.put(future) self.tts_queue.put((future, text_index))
self.dialogue.put(Message(role="assistant", content=text)) self.dialogue.put(Message(role="assistant", content=text))
else: else:
pass pass
@@ -801,7 +785,7 @@ class ConnectionHandler:
if future is None: if future is None:
continue continue
text = None text = None
opus_datas, tts_file = [], None opus_datas, tts_file = [], None
try: try:
self.logger.bind(tag=TAG).debug("正在处理TTS任务...") self.logger.bind(tag=TAG).debug("正在处理TTS任务...")
tts_timeout = int(self.config.get("tts_timeout", 10)) tts_timeout = int(self.config.get("tts_timeout", 10))
@@ -3,15 +3,16 @@ import queue
from config.logger import setup_logging from config.logger import setup_logging
TAG = __name__ TAG = __name__
logger = setup_logging()
async def handleAbortMessage(conn): async def handleAbortMessage(conn):
logger.bind(tag=TAG).info("Abort message received") conn.logger.bind(tag=TAG).info("Abort message received")
# 设置成打断状态,会自动打断llm、tts任务 # 设置成打断状态,会自动打断llm、tts任务
conn.client_abort = True conn.client_abort = True
conn.clear_queues() conn.clear_queues()
# 打断客户端说话状态 # 打断客户端说话状态
await conn.websocket.send(json.dumps({"type": "tts", "state": "stop", "session_id": conn.session_id})) await conn.websocket.send(
json.dumps({"type": "tts", "state": "stop", "session_id": conn.session_id})
)
conn.clearSpeakStatus() conn.clearSpeakStatus()
logger.bind(tag=TAG).info("Abort message received-end") conn.logger.bind(tag=TAG).info("Abort message received-end")
@@ -4,7 +4,6 @@ from plugins_func.register import FunctionRegistry, ActionResponse, Action, Tool
from plugins_func.functions.hass_init import append_devices_to_prompt from plugins_func.functions.hass_init import append_devices_to_prompt
TAG = __name__ TAG = __name__
logger = setup_logging()
class FunctionHandler: class FunctionHandler:
@@ -40,7 +39,9 @@ class FunctionHandler:
for func in self.functions_desc: for func in self.functions_desc:
func_names.append(func["function"]["name"]) func_names.append(func["function"]["name"])
# 打印当前支持的函数列表 # 打印当前支持的函数列表
logger.bind(tag=TAG).info(f"当前支持的函数列表: {func_names}") self.conn.logger.bind(tag=TAG, session_id=self.conn.session_id).info(
f"当前支持的函数列表: {func_names}"
)
return func_names return func_names
def get_functions(self): def get_functions(self):
@@ -79,7 +80,9 @@ class FunctionHandler:
func = funcItem.func func = funcItem.func
arguments = function_call_data["arguments"] arguments = function_call_data["arguments"]
arguments = json.loads(arguments) if arguments else {} arguments = json.loads(arguments) if arguments else {}
logger.bind(tag=TAG).debug(f"调用函数: {function_name}, 参数: {arguments}") self.conn.logger.bind(tag=TAG).debug(
f"调用函数: {function_name}, 参数: {arguments}"
)
if ( if (
funcItem.type == ToolType.SYSTEM_CTL funcItem.type == ToolType.SYSTEM_CTL
or funcItem.type == ToolType.IOT_CTL or funcItem.type == ToolType.IOT_CTL
@@ -94,6 +97,6 @@ class FunctionHandler:
action=Action.NOTFOUND, result="没有找到对应的函数", response="" action=Action.NOTFOUND, result="没有找到对应的函数", response=""
) )
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"处理function call错误: {e}") self.conn.logger.bind(tag=TAG).error(f"处理function call错误: {e}")
return None return None
@@ -9,7 +9,6 @@ import random
import time import time
TAG = __name__ TAG = __name__
logger = setup_logging()
WAKEUP_CONFIG = { WAKEUP_CONFIG = {
"dir": "config/assets/", "dir": "config/assets/",
@@ -75,7 +74,7 @@ async def wakeupWordsResponse(conn):
await asyncio.sleep(1) await asyncio.sleep(1)
wait_max_time -= 1 wait_max_time -= 1
if wait_max_time <= 0: if wait_max_time <= 0:
logger.bind(tag=TAG).error("连接对象没有llm") conn.logger.bind(tag=TAG).error("连接对象没有llm")
return return
"""唤醒词响应""" """唤醒词响应"""
@@ -5,10 +5,8 @@ from core.handle.sendAudioHandle import send_stt_message
from core.handle.helloHandle import checkWakeupWords from core.handle.helloHandle import checkWakeupWords
from core.utils.util import remove_punctuation_and_length from core.utils.util import remove_punctuation_and_length
from core.utils.dialogue import Message from core.utils.dialogue import Message
from loguru import logger
TAG = __name__ TAG = __name__
logger = setup_logging()
async def handle_user_intent(conn, text): async def handle_user_intent(conn, text):
@@ -36,7 +34,7 @@ async def check_direct_exit(conn, text):
cmd_exit = conn.cmd_exit cmd_exit = conn.cmd_exit
for cmd in cmd_exit: for cmd in cmd_exit:
if text == cmd: if text == cmd:
logger.bind(tag=TAG).info(f"识别到明确的退出命令: {text}") conn.logger.bind(tag=TAG).info(f"识别到明确的退出命令: {text}")
await send_stt_message(conn, text) await send_stt_message(conn, text)
await conn.close() await conn.close()
return True return True
@@ -46,7 +44,7 @@ async def check_direct_exit(conn, text):
async def analyze_intent_with_llm(conn, text): async def analyze_intent_with_llm(conn, text):
"""使用LLM分析用户意图""" """使用LLM分析用户意图"""
if not hasattr(conn, "intent") or not conn.intent: if not hasattr(conn, "intent") or not conn.intent:
logger.bind(tag=TAG).warning("意图识别服务未初始化") conn.logger.bind(tag=TAG).warning("意图识别服务未初始化")
return None return None
# 对话历史记录 # 对话历史记录
@@ -55,7 +53,7 @@ async def analyze_intent_with_llm(conn, text):
intent_result = await conn.intent.detect_intent(conn, dialogue.dialogue, text) intent_result = await conn.intent.detect_intent(conn, dialogue.dialogue, text)
return intent_result return intent_result
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"意图识别失败: {str(e)}") conn.logger.bind(tag=TAG).error(f"意图识别失败: {str(e)}")
return None return None
@@ -69,7 +67,7 @@ async def process_intent_result(conn, intent_result, original_text):
# 检查是否有function_call # 检查是否有function_call
if "function_call" in intent_data: if "function_call" in intent_data:
# 直接从意图识别获取了function_call # 直接从意图识别获取了function_call
logger.bind(tag=TAG).debug( conn.logger.bind(tag=TAG).debug(
f"检测到function_call格式的意图结果: {intent_data['function_call']['name']}" f"检测到function_call格式的意图结果: {intent_data['function_call']['name']}"
) )
function_name = intent_data["function_call"]["name"] function_name = intent_data["function_call"]["name"]
@@ -118,7 +116,7 @@ async def process_intent_result(conn, intent_result, original_text):
conn.speak_and_play, text, text_index conn.speak_and_play, text, text_index
) )
conn.llm_finish_task = True conn.llm_finish_task = True
conn.tts_queue.put(future) conn.tts_queue.put((future, text_index))
conn.dialogue.put(Message(role="assistant", content=text)) conn.dialogue.put(Message(role="assistant", content=text))
# 将函数执行放在线程池中 # 将函数执行放在线程池中
@@ -126,7 +124,7 @@ async def process_intent_result(conn, intent_result, original_text):
return True return True
return False return False
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
logger.bind(tag=TAG).error(f"处理意图结果时出错: {e}") conn.logger.bind(tag=TAG).error(f"处理意图结果时出错: {e}")
return False return False
+22 -19
View File
@@ -10,7 +10,6 @@ from plugins_func.register import (
) )
TAG = __name__ TAG = __name__
logger = setup_logging()
def wrap_async_function(async_func): def wrap_async_function(async_func):
@@ -21,7 +20,7 @@ def wrap_async_function(async_func):
# 获取连接对象(第一个参数) # 获取连接对象(第一个参数)
conn = args[0] conn = args[0]
if not hasattr(conn, "loop"): if not hasattr(conn, "loop"):
logger.bind(tag=TAG).error("Connection对象没有loop属性") conn.logger.bind(tag=TAG).error("Connection对象没有loop属性")
return ActionResponse( return ActionResponse(
Action.ERROR, Action.ERROR,
"Connection对象没有loop属性", "Connection对象没有loop属性",
@@ -35,7 +34,7 @@ def wrap_async_function(async_func):
# 等待结果返回 # 等待结果返回
return future.result() return future.result()
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"运行异步函数时出错: {e}") conn.logger.bind(tag=TAG).error(f"运行异步函数时出错: {e}")
return ActionResponse(Action.ERROR, str(e), f"执行操作时出错: {e}") return ActionResponse(Action.ERROR, str(e), f"执行操作时出错: {e}")
return wrapper return wrapper
@@ -57,7 +56,7 @@ def create_iot_function(device_name, method_name, method_info):
response_failure = "操作失败" response_failure = "操作失败"
# 打印响应参数 # 打印响应参数
logger.bind(tag=TAG).debug( conn.logger.bind(tag=TAG).debug(
f"控制函数接收到的响应参数: success='{response_success}', failure='{response_failure}'" f"控制函数接收到的响应参数: success='{response_success}', failure='{response_failure}'"
) )
@@ -86,7 +85,9 @@ def create_iot_function(device_name, method_name, method_info):
return ActionResponse(Action.RESPONSE, result, response) return ActionResponse(Action.RESPONSE, result, response)
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"执行{device_name}{method_name}操作失败: {e}") conn.logger.bind(tag=TAG).error(
f"执行{device_name}{method_name}操作失败: {e}"
)
# 操作失败时使用大模型提供的失败响应 # 操作失败时使用大模型提供的失败响应
response = response_failure response = response_failure
@@ -104,7 +105,7 @@ def create_iot_query_function(device_name, prop_name, prop_info):
async def iot_query_function(conn, response_success=None, response_failure=None): async def iot_query_function(conn, response_success=None, response_failure=None):
try: try:
# 打印响应参数 # 打印响应参数
logger.bind(tag=TAG).info( conn.logger.bind(tag=TAG).info(
f"查询函数接收到的响应参数: success='{response_success}', failure='{response_failure}'" f"查询函数接收到的响应参数: success='{response_success}', failure='{response_failure}'"
) )
@@ -122,7 +123,9 @@ def create_iot_query_function(device_name, prop_name, prop_info):
return ActionResponse(Action.ERROR, f"属性{prop_name}不存在", response) return ActionResponse(Action.ERROR, f"属性{prop_name}不存在", response)
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"查询{device_name}{prop_name}时出错: {e}") conn.logger.bind(tag=TAG).error(
f"查询{device_name}{prop_name}时出错: {e}"
)
# 查询出错时使用大模型提供的失败响应 # 查询出错时使用大模型提供的失败响应
response = response_failure response = response_failure
@@ -280,7 +283,7 @@ async def handleIotDescriptors(conn, descriptors):
await asyncio.sleep(1) await asyncio.sleep(1)
wait_max_time -= 1 wait_max_time -= 1
if wait_max_time <= 0: if wait_max_time <= 0:
logger.bind(tag=TAG).debug("连接对象没有func_handler") conn.logger.bind(tag=TAG).debug("连接对象没有func_handler")
return return
"""处理物联网描述""" """处理物联网描述"""
functions_changed = False functions_changed = False
@@ -323,7 +326,7 @@ async def handleIotDescriptors(conn, descriptors):
if hasattr(conn, "func_handler"): if hasattr(conn, "func_handler"):
for func_name in device_functions: for func_name in device_functions:
conn.func_handler.function_registry.register_function(func_name) conn.func_handler.function_registry.register_function(func_name)
logger.bind(tag=TAG).info( conn.logger.bind(tag=TAG).info(
f"注册IOT函数到function handler: {func_name}" f"注册IOT函数到function handler: {func_name}"
) )
functions_changed = True functions_changed = True
@@ -332,8 +335,8 @@ async def handleIotDescriptors(conn, descriptors):
if functions_changed and hasattr(conn, "func_handler"): if functions_changed and hasattr(conn, "func_handler"):
conn.func_handler.upload_functions_desc() conn.func_handler.upload_functions_desc()
func_names = conn.func_handler.current_support_functions() func_names = conn.func_handler.current_support_functions()
logger.bind(tag=TAG).info(f"设备类型: {type_id}") conn.logger.bind(tag=TAG).info(f"设备类型: {type_id}")
logger.bind(tag=TAG).info( conn.logger.bind(tag=TAG).info(
f"更新function描述列表完成,当前支持的函数: {func_names}" f"更新function描述列表完成,当前支持的函数: {func_names}"
) )
@@ -347,13 +350,13 @@ async def handleIotStatus(conn, states):
for k, v in state["state"].items(): for k, v in state["state"].items():
if property_item["name"] == k: if property_item["name"] == k:
if type(v) != type(property_item["value"]): if type(v) != type(property_item["value"]):
logger.bind(tag=TAG).error( conn.logger.bind(tag=TAG).error(
f"属性{property_item['name']}的值类型不匹配" f"属性{property_item['name']}的值类型不匹配"
) )
break break
else: else:
property_item["value"] = v property_item["value"] = v
logger.bind(tag=TAG).info( conn.logger.bind(tag=TAG).info(
f"物联网状态更新: {key} , {property_item['name']} = {v}" f"物联网状态更新: {key} , {property_item['name']} = {v}"
) )
break break
@@ -367,7 +370,7 @@ async def get_iot_status(conn, name, property_name):
for property_item in value.properties: for property_item in value.properties:
if property_item["name"] == property_name: if property_item["name"] == property_name:
return property_item["value"] return property_item["value"]
logger.bind(tag=TAG).warning(f"未找到设备 {name} 的属性 {property_name}") conn.logger.bind(tag=TAG).warning(f"未找到设备 {name} 的属性 {property_name}")
return None return None
@@ -378,16 +381,16 @@ async def set_iot_status(conn, name, property_name, value):
for property_item in iot_descriptor.properties: for property_item in iot_descriptor.properties:
if property_item["name"] == property_name: if property_item["name"] == property_name:
if type(value) != type(property_item["value"]): if type(value) != type(property_item["value"]):
logger.bind(tag=TAG).error( conn.logger.bind(tag=TAG).error(
f"属性{property_item['name']}的值类型不匹配" f"属性{property_item['name']}的值类型不匹配"
) )
return return
property_item["value"] = value property_item["value"] = value
logger.bind(tag=TAG).info( conn.logger.bind(tag=TAG).info(
f"物联网状态更新: {name} , {property_name} = {value}" f"物联网状态更新: {name} , {property_name} = {value}"
) )
return return
logger.bind(tag=TAG).warning(f"未找到设备 {name} 的属性 {property_name}") conn.logger.bind(tag=TAG).warning(f"未找到设备 {name} 的属性 {property_name}")
async def send_iot_conn(conn, name, method_name, parameters): async def send_iot_conn(conn, name, method_name, parameters):
@@ -409,6 +412,6 @@ async def send_iot_conn(conn, name, method_name, parameters):
command["parameters"] = parameters command["parameters"] = parameters
send_message = json.dumps({"type": "iot", "commands": [command]}) send_message = json.dumps({"type": "iot", "commands": [command]})
await conn.websocket.send(send_message) await conn.websocket.send(send_message)
logger.bind(tag=TAG).info(f"发送物联网指令: {send_message}") conn.logger.bind(tag=TAG).info(f"发送物联网指令: {send_message}")
return return
logger.bind(tag=TAG).error(f"未找到方法{method_name}") conn.logger.bind(tag=TAG).error(f"未找到方法{method_name}")
@@ -1,4 +1,3 @@
from config.logger import setup_logging
import time import time
import copy import copy
from core.utils.util import remove_punctuation_and_length from core.utils.util import remove_punctuation_and_length
@@ -9,12 +8,13 @@ from core.handle.ttsReportHandle import enqueue_tts_report
from core.providers.tts.base import audio_to_opus_data from core.providers.tts.base import audio_to_opus_data
TAG = __name__ TAG = __name__
logger = setup_logging()
async def handleAudioMessage(conn, audio): async def handleAudioMessage(conn, audio):
if conn.vad is None:
return
if not conn.asr_server_receive: if not conn.asr_server_receive:
logger.bind(tag=TAG).debug(f"前期数据处理中,暂停接收") conn.logger.bind(tag=TAG).debug(f"前期数据处理中,暂停接收")
return return
if conn.client_listen_mode == "auto": if conn.client_listen_mode == "auto":
have_voice = conn.vad.is_vad(conn, audio) have_voice = conn.vad.is_vad(conn, audio)
@@ -40,7 +40,7 @@ async def handleAudioMessage(conn, audio):
conn.asr_server_receive = True conn.asr_server_receive = True
else: else:
text, _ = await conn.asr.speech_to_text(conn.asr_audio, conn.session_id) text, _ = await conn.asr.speech_to_text(conn.asr_audio, conn.session_id)
logger.bind(tag=TAG).info(f"识别文本: {text}") conn.logger.bind(tag=TAG).info(f"识别文本: {text}")
text_len, _ = remove_punctuation_and_length(text) text_len, _ = remove_punctuation_and_length(text)
if text_len > 0: if text_len > 0:
# 使用自定义模块进行上报 # 使用自定义模块进行上报
@@ -120,7 +120,7 @@ async def check_bind_device(conn):
if conn.bind_code: if conn.bind_code:
# 确保bind_code是6位数字 # 确保bind_code是6位数字
if len(conn.bind_code) != 6: if len(conn.bind_code) != 6:
logger.bind(tag=TAG).error(f"无效的绑定码格式: {conn.bind_code}") conn.logger.bind(tag=TAG).error(f"无效的绑定码格式: {conn.bind_code}")
text = "绑定码格式错误,请检查配置。" text = "绑定码格式错误,请检查配置。"
await send_stt_message(conn, text) await send_stt_message(conn, text)
return return
@@ -144,7 +144,7 @@ async def check_bind_device(conn):
num_packets, _ = audio_to_opus_data(num_path) num_packets, _ = audio_to_opus_data(num_path)
conn.audio_play_queue.put((num_packets, None, i + 1)) conn.audio_play_queue.put((num_packets, None, i + 1))
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"播放数字音频失败: {e}") conn.logger.bind(tag=TAG).error(f"播放数字音频失败: {e}")
continue continue
else: else:
text = f"没有找到该设备的版本信息,请正确配置 OTA地址,然后重新编译固件。" text = f"没有找到该设备的版本信息,请正确配置 OTA地址,然后重新编译固件。"
@@ -1,11 +1,9 @@
from config.logger import setup_logging
import json import json
import asyncio import asyncio
import time import time
from core.utils.util import get_string_no_punctuation_or_emoji, analyze_emotion from core.utils.util import get_string_no_punctuation_or_emoji, analyze_emotion
TAG = __name__ TAG = __name__
logger = setup_logging()
emoji_map = { emoji_map = {
"neutral": "😶", "neutral": "😶",
@@ -49,10 +47,10 @@ async def sendAudioMessage(conn, audios, text, text_index=0):
) )
if text_index == conn.tts_first_text_index: if text_index == conn.tts_first_text_index:
logger.bind(tag=TAG).info(f"发送第一段语音: {text}") conn.logger.bind(tag=TAG).info(f"发送第一段语音: {text}")
await send_tts_message(conn, "sentence_start", text) await send_tts_message(conn, "sentence_start", text)
is_first_audio = (text_index == conn.tts_first_text_index) is_first_audio = text_index == conn.tts_first_text_index
await sendAudio(conn, audios, pre_buffer=is_first_audio) await sendAudio(conn, audios, pre_buffer=is_first_audio)
await send_tts_message(conn, "sentence_end", text) await send_tts_message(conn, "sentence_end", text)
@@ -1,4 +1,3 @@
from config.logger import setup_logging
import json import json
from core.handle.abortHandle import handleAbortMessage from core.handle.abortHandle import handleAbortMessage
from core.handle.helloHandle import handleHelloMessage from core.handle.helloHandle import handleHelloMessage
@@ -10,12 +9,11 @@ from core.handle.ttsReportHandle import enqueue_tts_report
import asyncio import asyncio
TAG = __name__ TAG = __name__
logger = setup_logging()
async def handleTextMessage(conn, message): async def handleTextMessage(conn, message):
"""处理文本消息""" """处理文本消息"""
logger.bind(tag=TAG).info(f"收到文本消息:{message}") conn.logger.bind(tag=TAG).info(f"收到文本消息:{message}")
try: try:
msg_json = json.loads(message) msg_json = json.loads(message)
if isinstance(msg_json, int): if isinstance(msg_json, int):
@@ -28,7 +26,9 @@ async def handleTextMessage(conn, message):
elif msg_json["type"] == "listen": elif msg_json["type"] == "listen":
if "mode" in msg_json: if "mode" in msg_json:
conn.client_listen_mode = msg_json["mode"] conn.client_listen_mode = msg_json["mode"]
logger.bind(tag=TAG).debug(f"客户端拾音模式:{conn.client_listen_mode}") conn.logger.bind(tag=TAG).debug(
f"客户端拾音模式:{conn.client_listen_mode}"
)
if msg_json["state"] == "start": if msg_json["state"] == "start":
conn.client_have_voice = True conn.client_have_voice = True
conn.client_voice_stop = False conn.client_voice_stop = False
@@ -9,16 +9,11 @@ TTS上报功能已集成到ConnectionHandler类中。
具体实现请参考core/connection.py中的相关代码。 具体实现请参考core/connection.py中的相关代码。
""" """
import os
import uuid
import wave
import opuslib_next import opuslib_next
from config.logger import setup_logging
from config.manage_api_client import report from config.manage_api_client import report
TAG = __name__ TAG = __name__
logger = setup_logging()
def report_tts(conn, type, text, opus_data): def report_tts(conn, type, text, opus_data):
@@ -32,7 +27,7 @@ def report_tts(conn, type, text, opus_data):
""" """
try: try:
if opus_data: if opus_data:
audio_data = opus_to_wav(opus_data) audio_data = opus_to_wav(conn, opus_data)
else: else:
audio_data = None audio_data = None
# 执行上报 # 执行上报
@@ -44,10 +39,10 @@ def report_tts(conn, type, text, opus_data):
audio=audio_data, audio=audio_data,
) )
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"TTS上报失败: {e}") conn.logger.bind(tag=TAG).error(f"TTS上报失败: {e}")
def opus_to_wav(opus_data): def opus_to_wav(conn, opus_data):
"""将Opus数据转换为WAV格式的字节流 """将Opus数据转换为WAV格式的字节流
Args: Args:
@@ -65,7 +60,7 @@ def opus_to_wav(opus_data):
pcm_frame = decoder.decode(opus_packet, 960) # 960 samples = 60ms pcm_frame = decoder.decode(opus_packet, 960) # 960 samples = 60ms
pcm_data.append(pcm_frame) pcm_data.append(pcm_frame)
except opuslib_next.OpusError as e: except opuslib_next.OpusError as e:
logger.bind(tag=TAG).error(f"Opus解码错误: {e}", exc_info=True) conn.logger.bind(tag=TAG).error(f"Opus解码错误: {e}", exc_info=True)
if not pcm_data: if not pcm_data:
raise ValueError("没有有效的PCM数据") raise ValueError("没有有效的PCM数据")
@@ -108,8 +103,8 @@ def enqueue_tts_report(conn, type, text, opus_data):
# 使用连接对象的队列,传入文本和二进制数据而非文件路径 # 使用连接对象的队列,传入文本和二进制数据而非文件路径
conn.tts_report_queue.put((type, text, opus_data)) conn.tts_report_queue.put((type, text, opus_data))
logger.bind(tag=TAG).debug( conn.logger.bind(tag=TAG).debug(
f"TTS数据已加入上报队列: {conn.device_id}, 音频大小: {len(opus_data)} " f"TTS数据已加入上报队列: {conn.device_id}, 音频大小: {len(opus_data)} "
) )
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"加入TTS上报队列失败: {text}, {e}") conn.logger.bind(tag=TAG).error(f"加入TTS上报队列失败: {text}, {e}")
+11 -12
View File
@@ -1,9 +1,9 @@
"""MCP服务管理器""" """MCP服务管理器"""
import asyncio import asyncio
import os, json import os, json
from typing import Dict, Any, List from typing import Dict, Any, List
from .MCPClient import MCPClient from .MCPClient import MCPClient
from config.logger import setup_logging
from plugins_func.register import register_function, ToolType from plugins_func.register import register_function, ToolType
from config.config_loader import get_project_dir from config.config_loader import get_project_dir
@@ -18,11 +18,10 @@ class MCPManager:
初始化MCP管理器 初始化MCP管理器
""" """
self.conn = conn self.conn = conn
self.logger = setup_logging()
self.config_path = get_project_dir() + "data/.mcp_server_settings.json" self.config_path = get_project_dir() + "data/.mcp_server_settings.json"
if os.path.exists(self.config_path) == False: if os.path.exists(self.config_path) == False:
self.config_path = "" self.config_path = ""
self.logger.bind(tag=TAG).warning( self.conn.logger.bind(tag=TAG).warning(
f"请检查mcp服务配置文件:data/.mcp_server_settings.json" f"请检查mcp服务配置文件:data/.mcp_server_settings.json"
) )
self.client: Dict[str, MCPClient] = {} self.client: Dict[str, MCPClient] = {}
@@ -41,7 +40,7 @@ class MCPManager:
config = json.load(f) config = json.load(f)
return config.get("mcpServers", {}) return config.get("mcpServers", {})
except Exception as e: except Exception as e:
self.logger.bind(tag=TAG).error( self.conn.logger.bind(tag=TAG).error(
f"Error loading MCP config from {self.config_path}: {e}" f"Error loading MCP config from {self.config_path}: {e}"
) )
return {} return {}
@@ -51,7 +50,7 @@ class MCPManager:
config = self.load_config() config = self.load_config()
for name, srv_config in config.items(): for name, srv_config in config.items():
if not srv_config.get("command") and not srv_config.get("url"): if not srv_config.get("command") and not srv_config.get("url"):
self.logger.bind(tag=TAG).warning( self.conn.logger.bind(tag=TAG).warning(
f"Skipping server {name}: neither command nor url specified" f"Skipping server {name}: neither command nor url specified"
) )
continue continue
@@ -60,7 +59,7 @@ class MCPManager:
client = MCPClient(srv_config) client = MCPClient(srv_config)
await client.initialize() await client.initialize()
self.client[name] = client self.client[name] = client
self.logger.bind(tag=TAG).info(f"Initialized MCP client: {name}") self.conn.logger.bind(tag=TAG).info(f"Initialized MCP client: {name}")
client_tools = client.get_available_tools() client_tools = client.get_available_tools()
self.tools.extend(client_tools) self.tools.extend(client_tools)
for tool in client_tools: for tool in client_tools:
@@ -73,7 +72,7 @@ class MCPManager:
) )
except Exception as e: except Exception as e:
self.logger.bind(tag=TAG).error( self.conn.logger.bind(tag=TAG).error(
f"Failed to initialize MCP server {name}: {e}" f"Failed to initialize MCP server {name}: {e}"
) )
self.conn.func_handler.upload_functions_desc() self.conn.func_handler.upload_functions_desc()
@@ -94,8 +93,8 @@ class MCPManager:
""" """
for tool in self.tools: for tool in self.tools:
if ( if (
tool.get("function") != None tool.get("function") != None
and tool["function"].get("name") == tool_name and tool["function"].get("name") == tool_name
): ):
return True return True
return False return False
@@ -110,7 +109,7 @@ class MCPManager:
Raises: Raises:
ValueError: 工具未找到时抛出 ValueError: 工具未找到时抛出
""" """
self.logger.bind(tag=TAG).info( self.conn.logger.bind(tag=TAG).info(
f"Executing tool {tool_name} with arguments: {arguments}" f"Executing tool {tool_name} with arguments: {arguments}"
) )
for client in self.client.values(): for client in self.client.values():
@@ -124,9 +123,9 @@ class MCPManager:
for name, client in list(self.client.items()): for name, client in list(self.client.items()):
try: try:
await asyncio.wait_for(client.cleanup(), timeout=20) await asyncio.wait_for(client.cleanup(), timeout=20)
self.logger.bind(tag=TAG).info(f"MCP client closed: {name}") self.conn.logger.bind(tag=TAG).info(f"MCP client closed: {name}")
except (asyncio.TimeoutError, Exception) as e: except (asyncio.TimeoutError, Exception) as e:
self.logger.bind(tag=TAG).error( self.conn.logger.bind(tag=TAG).error(
f"Error closing MCP client {name}: {e}" f"Error closing MCP client {name}: {e}"
) )
self.client.clear() self.client.clear()
+18 -9
View File
@@ -4,7 +4,14 @@ from datetime import datetime
class Message: class Message:
def __init__(self, role: str, content: str = None, uniq_id: str = None, tool_calls = None, tool_call_id=None): def __init__(
self,
role: str,
content: str = None,
uniq_id: str = None,
tool_calls=None,
tool_call_id=None,
):
self.uniq_id = uniq_id if uniq_id is not None else str(uuid.uuid4()) self.uniq_id = uniq_id if uniq_id is not None else str(uuid.uuid4())
self.role = role self.role = role
self.content = content self.content = content
@@ -16,7 +23,7 @@ class Dialogue:
def __init__(self): def __init__(self):
self.dialogue: List[Message] = [] self.dialogue: List[Message] = []
# 获取当前时间 # 获取当前时间
self.current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') self.current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def put(self, message: Message): def put(self, message: Message):
self.dialogue.append(message) self.dialogue.append(message)
@@ -25,7 +32,9 @@ class Dialogue:
if m.tool_calls is not None: if m.tool_calls is not None:
dialogue.append({"role": m.role, "tool_calls": m.tool_calls}) dialogue.append({"role": m.role, "tool_calls": m.tool_calls})
elif m.role == "tool": elif m.role == "tool":
dialogue.append({"role": m.role, "tool_call_id": m.tool_call_id, "content": m.content}) dialogue.append(
{"role": m.role, "tool_call_id": m.tool_call_id, "content": m.content}
)
else: else:
dialogue.append({"role": m.role, "content": m.content}) dialogue.append({"role": m.role, "content": m.content})
@@ -44,23 +53,23 @@ class Dialogue:
else: else:
self.put(Message(role="system", content=new_content)) self.put(Message(role="system", content=new_content))
def get_llm_dialogue_with_memory(self, memory_str: str = None) -> List[Dict[str, str]]: def get_llm_dialogue_with_memory(
self, memory_str: str = None
) -> List[Dict[str, str]]:
if memory_str is None or len(memory_str) == 0: if memory_str is None or len(memory_str) == 0:
return self.get_llm_dialogue() return self.get_llm_dialogue()
# 构建带记忆的对话 # 构建带记忆的对话
dialogue = [] dialogue = []
# 添加系统提示和记忆 # 添加系统提示和记忆
system_message = next( system_message = next(
(msg for msg in self.dialogue if msg.role == "system"), None (msg for msg in self.dialogue if msg.role == "system"), None
) )
if system_message: if system_message:
enhanced_system_prompt = ( enhanced_system_prompt = (
f"{system_message.content}\n\n" f"{system_message.content}\n\n" f"相关记忆:\n{memory_str}"
f"相关记忆:\n{memory_str}"
) )
dialogue.append({"role": "system", "content": enhanced_system_prompt}) dialogue.append({"role": "system", "content": enhanced_system_prompt})
+42 -6
View File
@@ -350,12 +350,6 @@ def initialize_modules(
str(config.get("delete_audio", True)).lower() in ("true", "1", "yes"), str(config.get("delete_audio", True)).lower() in ("true", "1", "yes"),
) )
logger.bind(tag=TAG).info(f"初始化组件: asr成功 {select_asr_module}") logger.bind(tag=TAG).info(f"初始化组件: asr成功 {select_asr_module}")
# 初始化自定义prompt
if config.get("prompt", None) is not None:
modules["prompt"] = config["prompt"]
logger.bind(tag=TAG).info(f"初始化组件: prompt成功 {modules['prompt'][:50]}...")
return modules return modules
@@ -913,3 +907,45 @@ def audio_to_opus_data(audio_file_path):
opus_datas.append(opus_data) opus_datas.append(opus_data)
return opus_datas, duration return opus_datas, duration
def check_vad_update(before_config, new_config):
if new_config["selected_module"].get("VAD") is None:
return False
update_vad = False
current_vad_module = before_config["selected_module"]["VAD"]
new_vad_module = new_config["selected_module"]["VAD"]
current_vad_type = (
current_vad_module
if "type" not in before_config["VAD"][current_vad_module]
else before_config["VAD"][current_vad_module]["type"]
)
new_vad_type = (
new_vad_module
if "type" not in new_config["VAD"][new_vad_module]
else new_config["VAD"][new_vad_module]["type"]
)
print(f"前vad:{current_vad_type},后vad:{new_vad_type}")
update_vad = current_vad_type != new_vad_type
return update_vad
def check_asr_update(before_config, new_config):
if new_config["selected_module"].get("ASR") is None:
return False
update_asr = False
current_asr_module = before_config["selected_module"]["ASR"]
new_asr_module = new_config["selected_module"]["ASR"]
current_asr_type = (
current_asr_module
if "type" not in before_config["ASR"][current_asr_module]
else before_config["ASR"][current_asr_module]["type"]
)
new_asr_type = (
new_asr_module
if "type" not in new_config["ASR"][new_asr_module]
else new_config["ASR"][new_asr_module]["type"]
)
print(f"前asr:{current_asr_type},后asr:{new_asr_type}")
update_asr = current_asr_type != new_asr_type
return update_asr
+15 -39
View File
@@ -2,7 +2,7 @@ import asyncio
import websockets import websockets
from config.logger import setup_logging from config.logger import setup_logging
from core.connection import ConnectionHandler from core.connection import ConnectionHandler
from core.utils.util import initialize_modules from core.utils.util import initialize_modules, check_vad_update, check_asr_update
from config.config_loader import get_config_from_api from config.config_loader import get_config_from_api
TAG = __name__ TAG = __name__
@@ -84,38 +84,8 @@ class WebSocketServer:
return False return False
# 检查 VAD 和 ASR 类型是否需要更新 # 检查 VAD 和 ASR 类型是否需要更新
update_vad = False update_vad = check_vad_update(self.config, new_config)
update_asr = False update_asr = check_asr_update(self.config, new_config)
# 获取当前和新的 VAD 类型
current_vad_module = self.config["selected_module"]["VAD"]
new_vad_module = new_config["selected_module"]["VAD"]
current_vad_type = (
current_vad_module
if "type" not in self.config["VAD"][current_vad_module]
else self.config["VAD"][current_vad_module]["type"]
)
new_vad_type = (
new_vad_module
if "type" not in new_config["VAD"][new_vad_module]
else new_config["VAD"][new_vad_module]["type"]
)
update_vad = current_vad_type != new_vad_type
# 获取当前和新的 ASR 类型
current_asr_module = self.config["selected_module"]["ASR"]
new_asr_module = new_config["selected_module"]["ASR"]
current_asr_type = (
current_asr_module
if "type" not in self.config["ASR"][current_asr_module]
else self.config["ASR"][current_asr_module]["type"]
)
new_asr_type = (
new_asr_module
if "type" not in new_config["ASR"][new_asr_module]
else new_config["ASR"][new_asr_module]["type"]
)
update_asr = current_asr_type != new_asr_type
# 更新配置 # 更新配置
self.config = new_config self.config = new_config
@@ -132,12 +102,18 @@ class WebSocketServer:
) )
# 更新组件实例 # 更新组件实例
self._vad = modules["vad"] if "vad" in modules else None if "vad" in modules:
self._asr = modules["asr"] if "asr" in modules else None self._vad = modules["vad"]
self._tts = modules["tts"] if "tts" in modules else None if "asr" in modules:
self._llm = modules["llm"] if "llm" in modules else None self._asr = modules["asr"]
self._intent = modules["intent"] if "intent" in modules else None if "tts" in modules:
self._memory = modules["memory"] if "memory" in modules else None self._tts = modules["tts"]
if "llm" in modules:
self._llm = modules["llm"]
if "intent" in modules:
self._intent = modules["intent"]
if "memory" in modules:
self._memory = modules["memory"]
return True return True
except Exception as e: except Exception as e:
@@ -11,9 +11,7 @@ from core.utils import p3
from core.handle.sendAudioHandle import send_stt_message from core.handle.sendAudioHandle import send_stt_message
from plugins_func.register import register_function, ToolType, ActionResponse, Action from plugins_func.register import register_function, ToolType, ActionResponse, Action
TAG = __name__ TAG = __name__
logger = setup_logging()
MUSIC_CACHE = {} MUSIC_CACHE = {}
@@ -45,7 +43,7 @@ def play_music(conn, song_name: str):
# 检查事件循环状态 # 检查事件循环状态
if not conn.loop.is_running(): if not conn.loop.is_running():
logger.bind(tag=TAG).error("事件循环未运行,无法提交任务") conn.logger.bind(tag=TAG).error("事件循环未运行,无法提交任务")
return ActionResponse( return ActionResponse(
action=Action.RESPONSE, result="系统繁忙", response="请稍后再试" action=Action.RESPONSE, result="系统繁忙", response="请稍后再试"
) )
@@ -59,9 +57,9 @@ def play_music(conn, song_name: str):
def handle_done(f): def handle_done(f):
try: try:
f.result() # 可在此处理成功逻辑 f.result() # 可在此处理成功逻辑
logger.bind(tag=TAG).info("播放完成") conn.logger.bind(tag=TAG).info("播放完成")
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"播放失败: {e}") conn.logger.bind(tag=TAG).error(f"播放失败: {e}")
future.add_done_callback(handle_done) future.add_done_callback(handle_done)
@@ -69,7 +67,7 @@ def play_music(conn, song_name: str):
action=Action.NONE, result="指令已接收", response="正在为您播放音乐" action=Action.NONE, result="指令已接收", response="正在为您播放音乐"
) )
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"处理音乐意图错误: {e}") conn.logger.bind(tag=TAG).error(f"处理音乐意图错误: {e}")
return ActionResponse( return ActionResponse(
action=Action.RESPONSE, result=str(e), response="播放音乐时出错了" action=Action.RESPONSE, result=str(e), response="播放音乐时出错了"
) )
@@ -150,7 +148,7 @@ async def handle_music_command(conn, text):
"""处理音乐播放指令""" """处理音乐播放指令"""
clean_text = re.sub(r"[^\w\s]", "", text).strip() clean_text = re.sub(r"[^\w\s]", "", text).strip()
logger.bind(tag=TAG).debug(f"检查是否是音乐命令: {clean_text}") conn.logger.bind(tag=TAG).debug(f"检查是否是音乐命令: {clean_text}")
# 尝试匹配具体歌名 # 尝试匹配具体歌名
if os.path.exists(MUSIC_CACHE["music_dir"]): if os.path.exists(MUSIC_CACHE["music_dir"]):
@@ -165,7 +163,7 @@ async def handle_music_command(conn, text):
if potential_song: if potential_song:
best_match = _find_best_match(potential_song, MUSIC_CACHE["music_files"]) best_match = _find_best_match(potential_song, MUSIC_CACHE["music_files"])
if best_match: if best_match:
logger.bind(tag=TAG).info(f"找到最匹配的歌曲: {best_match}") conn.logger.bind(tag=TAG).info(f"找到最匹配的歌曲: {best_match}")
await play_local_music(conn, specific_file=best_match) await play_local_music(conn, specific_file=best_match)
return True return True
# 检查是否是通用播放音乐命令 # 检查是否是通用播放音乐命令
@@ -195,7 +193,9 @@ async def play_local_music(conn, specific_file=None):
"""播放本地音乐文件""" """播放本地音乐文件"""
try: try:
if not os.path.exists(MUSIC_CACHE["music_dir"]): if not os.path.exists(MUSIC_CACHE["music_dir"]):
logger.bind(tag=TAG).error(f"音乐目录不存在: " + MUSIC_CACHE["music_dir"]) conn.logger.bind(tag=TAG).error(
f"音乐目录不存在: " + MUSIC_CACHE["music_dir"]
)
return return
# 确保路径正确性 # 确保路径正确性
@@ -204,13 +204,13 @@ async def play_local_music(conn, specific_file=None):
music_path = os.path.join(MUSIC_CACHE["music_dir"], specific_file) music_path = os.path.join(MUSIC_CACHE["music_dir"], specific_file)
else: else:
if not MUSIC_CACHE["music_files"]: if not MUSIC_CACHE["music_files"]:
logger.bind(tag=TAG).error("未找到MP3音乐文件") conn.logger.bind(tag=TAG).error("未找到MP3音乐文件")
return return
selected_music = random.choice(MUSIC_CACHE["music_files"]) selected_music = random.choice(MUSIC_CACHE["music_files"])
music_path = os.path.join(MUSIC_CACHE["music_dir"], selected_music) music_path = os.path.join(MUSIC_CACHE["music_dir"], selected_music)
if not os.path.exists(music_path): if not os.path.exists(music_path):
logger.bind(tag=TAG).error(f"选定的音乐文件不存在: {music_path}") conn.logger.bind(tag=TAG).error(f"选定的音乐文件不存在: {music_path}")
return return
text = _get_random_play_prompt(selected_music) text = _get_random_play_prompt(selected_music)
await send_stt_message(conn, text) await send_stt_message(conn, text)
@@ -233,5 +233,5 @@ async def play_local_music(conn, specific_file=None):
conn.audio_play_queue.put((opus_packets, None, conn.tts_last_text_index)) conn.audio_play_queue.put((opus_packets, None, conn.tts_last_text_index))
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"播放音乐失败: {str(e)}") conn.logger.bind(tag=TAG).error(f"播放音乐失败: {str(e)}")
logger.bind(tag=TAG).error(f"详细错误: {traceback.format_exc()}") conn.logger.bind(tag=TAG).error(f"详细错误: {traceback.format_exc()}")
@@ -1,51 +1,56 @@
from plugins_func.register import register_function,ToolType, ActionResponse, Action from plugins_func.register import register_function, ToolType, ActionResponse, Action
from config.logger import setup_logging
TAG = __name__
logger = setup_logging()
plugin_loader_function_desc = { plugin_loader_function_desc = {
"type": "function", "type": "function",
"function": { "function": {
"name": "plugin_loader", "name": "plugin_loader",
"description": "当用户想加载或卸载插件/function时,调用此函数:支持的插件列表为[plugins]", "description": "当用户想加载或卸载插件/function时,调用此函数:支持的插件列表为[plugins]",
"parameters": { "parameters": {
"type": "object", "type": "object",
"properties": { "properties": {
"oper": { "oper": {"type": "string", "description": "load or unload"},
"type": "string", "name": {"type": "string", "description": "要加载或卸载的插件名字"},
"description": "load or unload" },
}, "required": ["oper", "name"],
"name":{ },
"type": "string", },
"description": "要加载或卸载的插件名字" }
}
},
"required": ["oper","name"]
}
}
}
@register_function('plugin_loader', plugin_loader_function_desc, ToolType.SYSTEM_CTL)
@register_function("plugin_loader", plugin_loader_function_desc, ToolType.SYSTEM_CTL)
def plugin_loader(conn, oper: str, name: str): def plugin_loader(conn, oper: str, name: str):
"""插件加载""" """插件加载"""
if oper not in ["load", "unload"]: if oper not in ["load", "unload"]:
return ActionResponse(action=Action.RESPONSE, result="插件操作失败", response="不支持的操作") return ActionResponse(
action=Action.RESPONSE, result="插件操作失败", response="不支持的操作"
)
cur_support = conn.func_handler.current_support_functions() cur_support = conn.func_handler.current_support_functions()
if oper == "load": if oper == "load":
if name in cur_support: if name in cur_support:
return ActionResponse(action=Action.RESPONSE, result="插件加载失败", response=f"{name}插件已加载,无需重复加载") return ActionResponse(
action=Action.RESPONSE,
result="插件加载失败",
response=f"{name}插件已加载,无需重复加载",
)
func = conn.func_handler.function_registry.register_function(name) func = conn.func_handler.function_registry.register_function(name)
if not func: if not func:
return ActionResponse(action=Action.RESPONSE, result="插件加载失败", response="插件未找到") return ActionResponse(
action=Action.RESPONSE, result="插件加载失败", response="插件未找到"
)
res = f"{name}插件加载成功" res = f"{name}插件加载成功"
else: else:
if name not in cur_support: if name not in cur_support:
return ActionResponse(action=Action.RESPONSE, result="插件卸载失败", response=f"{name}插件未加载") return ActionResponse(
action=Action.RESPONSE,
result="插件卸载失败",
response=f"{name}插件未加载",
)
bOK = conn.func_handler.function_registry.unregister_function(name) bOK = conn.func_handler.function_registry.unregister_function(name)
if not bOK: if not bOK:
return ActionResponse(action=Action.RESPONSE, result="插件卸载失败", response="插件未找到") return ActionResponse(
action=Action.RESPONSE, result="插件卸载失败", response="插件未找到"
)
res = f"{name}插件卸载成功" res = f"{name}插件卸载成功"
conn.func_handler.upload_functions_desc() conn.func_handler.upload_functions_desc()
return ActionResponse(action=Action.RESPONSE, result="插件操作成功", response=res) return ActionResponse(action=Action.RESPONSE, result="插件操作成功", response=res)