merge:main分支的manager-web和xiaozhi-server

This commit is contained in:
hrz
2025-03-29 10:49:55 +08:00
parent fe87889ccd
commit 2988e5eef2
31 changed files with 1269 additions and 1620 deletions
+22 -13
View File
@@ -82,9 +82,10 @@ selected_module:
TTS: EdgeTTS
# 记忆模块,默认不开启记忆;如果想使用超长记忆,推荐使用mem0ai;如果注重隐私,请使用本地的mem_local_short
Memory: nomem
# 意图识别模块,默认使用function_call。开启后,可以播放音乐、控制音量、识别退出指令
# 意图识别使用intent_llm,优点:通用性强,缺点:增加串行前置意图识别模块,会增加处理时间
# 意图识别使用function_call,缺点:需要所选择的LLM支持function_call,优点:按需调用工具、速度快
# 意图识别模块开启后,可以播放音乐、控制音量、识别退出指令
# 不想开通意图识别,就设置成:nointent
# 意图识别使用intent_llm,如果你的LLM是DifyLLM或CozeLLM,建议使用这个。优点:通用性强,缺点:增加串行前置意图识别模块,会增加处理时间,这个意图识别暂时不支持控制音量大小等iot操作
# 意图识别可使用function_call,缺点:需要所选择的LLM支持function_call,优点:按需调用工具、速度快,理论上能全部操作所有iot指令
# 默认免费的ChatGLMLLM就已经支持function_call,但是如果像追求稳定建议把LLM设置成:DoubaoLLM,使用的具体model_name是:doubao-pro-32k-functioncall-241028
Intent: function_call
@@ -97,9 +98,13 @@ Intent:
intent_llm:
# 不需要动type
type: intent_llm
# 配备意图识别独立的思考模型
# 如果这里不填,则会默认使用selected_module.LLM的模型作为意图识别的思考模型
# 如果你的selected_module.LLM选择了DifyLLM或CozeLLM,这里最好使用独立的LLM作为意图识别,例如使用免费的ChatGLMLLM
llm: ChatGLMLLM
function_call:
# 不需要动type
type: nointent
type: function_call
# plugins_func/functions下的模块,可以通过配置,选择加载哪个模块,加载后对话支持相应的function调用
# 系统默认已经记载“handle_exit_intent(退出识别)”、“play_music(音乐播放)”插件,请勿重复加载
# 下面是加载查天气、角色切换、加载查新闻的插件示例
@@ -187,7 +192,7 @@ LLM:
# 可在这里找到你的 api_key https://bailian.console.aliyun.com/?apiKey=1#/api-key
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
model_name: qwen-turbo
api_key: 你的ali api key
api_key: 你的deepseek web key
temperature: 0.7 # 温度值
max_tokens: 500 # 最大生成token数
top_p: 1
@@ -278,6 +283,18 @@ LLM:
variables:
k: "v"
k2: "v2"
XinferenceLLM:
# 定义LLM API类型
type: xinference
# Xinference服务地址和模型名称
model_name: qwen2.5:72b-AWQ # 使用的模型名称,需要预先在Xinference启动对应模型
base_url: http://localhost:9997 # Xinference服务地址
XinferenceSmallLLM:
# 定义轻量级LLM API类型,用于意图识别
type: xinference
# Xinference服务地址和模型名称
model_name: qwen2.5:3b-AWQ # 使用的小模型名称,用于意图识别
base_url: http://localhost:9997 # Xinference服务地址
TTS:
# 当前支持的type为edge、doubao,可自行适配
EdgeTTS:
@@ -535,11 +552,3 @@ wakeup_words:
- "喵喵同学"
- "小滨小滨"
- "小冰小冰"
# 是否使用私有配置
use_private_config: true
# 远程配置
# 数据格式:{"code": 0,"msg": "success","data": {"prompt": "我是小智","owner": "用户ID","ASR": {"FunASR": {}},"LLM": {"ChatGLMLLM": {}},"TTS": {"EdgeTTS": {}},"VAD": {"SileroVAD": {}},"Intent": {"function_call": {}},"Memory": {"nomem": {}},"selected_module": {"ASR": "FunASR","LLM": "ChatGLMLLM","TTS": "EdgeTTS","Intent": "function_call","Memory": "nomem","VAD": "SileroVAD"}}}
remote_config:
enabled: true
url: http://192.168.5.11:8002/xiaozhi-esp32-api/api/v1/user/agent/loadAgentConfig/
+4 -73
View File
@@ -1,8 +1,6 @@
import os
import time
import yaml
import json
import requests
from config.logger import setup_logging
from typing import Dict, Any, Optional
from copy import deepcopy
@@ -24,14 +22,6 @@ class PrivateConfig:
async def load_or_create(self):
try:
# 优先通过远程获取配置
fetch_config_ = {}
remote_config = self.default_config['remote_config']
self.logger.bind(tag=TAG).info(f"remote config: {remote_config}")
if remote_config and remote_config['enabled']:
fetch_config_ = await self.fetch_config(remote_config['url'])
self.logger.bind(tag=TAG).info(f"fetch_config: {fetch_config_}")
await self.lock_manager.acquire_lock(self.config_path)
try:
if os.path.exists(self.config_path):
@@ -40,9 +30,6 @@ class PrivateConfig:
else:
all_configs = {}
if fetch_config_:
all_configs[self.device_id] = fetch_config_
if self.device_id not in all_configs:
# Get selected module names
selected_modules = self.default_config['selected_module']
@@ -77,9 +64,9 @@ class PrivateConfig:
all_configs[self.device_id] = device_config
# Save updated configs
with open(self.config_path, 'w', encoding='utf-8') as f:
yaml.dump(all_configs, f, allow_unicode=True)
# Save updated configs
with open(self.config_path, 'w', encoding='utf-8') as f:
yaml.dump(all_configs, f, allow_unicode=True)
self.private_config = all_configs[self.device_id]
@@ -251,60 +238,4 @@ class PrivateConfig:
def get_owner(self) -> Optional[str]:
"""获取设备当前所有者"""
return self.private_config.get('owner')
async def fetch_config(self, fetch_url: str) -> Dict[str, Any]:
"""通过HTTP请求远程获取配置"""
url = f"{fetch_url}{self.device_id}"
headers = {
'device_id': self.device_id
}
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # 如果响应状态码不是200,会抛出异常
# 检查响应内容类型是否为JSON
if response.headers.get('Content-Type') != 'application/json':
self.logger.bind(tag=TAG).error("Invalid content type: expected application/json")
return {}
# 解析JSON数据
config_data = response.json()
# 验证返回的数据结构
if not isinstance(config_data, dict):
self.logger.bind(tag=TAG).error("Invalid data format: expected a dictionary")
return {}
if config_data['code'] != 0:
self.logger.bind(tag=TAG).error(f"Fetch config error: {config_data['msg']}")
return {}
config_data_ = config_data['data']
if not isinstance(config_data_, dict):
self.logger.bind(tag=TAG).error("Invalid config data format: expected a dictionary")
return {}
# 检查必要的字段是否存在
required_fields = ['selected_module', 'prompt', 'LLM', 'TTS', 'ASR', 'VAD']
for field in required_fields:
if field not in config_data_:
self.logger.bind(tag=TAG).error(f"Missing required field: {field}")
return {}
# 检查每个模块的配置是否正确
for module in ['LLM', 'TTS', 'ASR', 'VAD']:
if not isinstance(config_data_[module], dict):
self.logger.bind(tag=TAG).error(f"Invalid data format for {module}: expected a dictionary")
return {}
selected_module = config_data_['selected_module'].get(module)
if selected_module not in config_data_[module]:
self.logger.bind(tag=TAG).error(f"Selected {module} not found in config: {selected_module}")
return {}
return config_data_
except requests.exceptions.RequestException as e:
self.logger.bind(tag=TAG).error(f"Error fetching config: {e}")
return {}
return self.private_config.get('owner')
@@ -1,18 +0,0 @@
'''
自定义记忆,可以选择此模块
'''
from ..base import MemoryProviderBase, logger
TAG = __name__
class MemoryProvider(MemoryProviderBase):
def __init__(self, config):
super().__init__(config)
async def save_memory(self, msgs):
logger.bind(tag=TAG).debug("mem_custom mode: Custom memory saving is performed.")
return None
async def query_memory(self, query: str)-> str:
logger.bind(tag=TAG).debug("mem_custom mode: Custom memory query is performed.")
return ""