初始化
This commit is contained in:
39
src/plugins/built_in/core_actions/_manifest.json
Normal file
39
src/plugins/built_in/core_actions/_manifest.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "核心动作插件 (Core Actions)",
|
||||
"version": "1.0.0",
|
||||
"description": "系统核心动作插件,提供基础聊天交互功能,包括回复、不回复、表情包发送和聊天模式切换等核心功能。",
|
||||
"author": {
|
||||
"name": "MaiBot团队",
|
||||
"url": "https://github.com/MaiM-with-u"
|
||||
},
|
||||
"license": "GPL-v3.0-or-later",
|
||||
|
||||
"host_application": {
|
||||
"min_version": "0.8.0"
|
||||
},
|
||||
"homepage_url": "https://github.com/MaiM-with-u/maibot",
|
||||
"repository_url": "https://github.com/MaiM-with-u/maibot",
|
||||
"keywords": ["core", "chat", "reply", "emoji", "action", "built-in"],
|
||||
"categories": ["Core System", "Chat Management"],
|
||||
|
||||
"default_locale": "zh-CN",
|
||||
"locales_path": "_locales",
|
||||
|
||||
"plugin_info": {
|
||||
"is_built_in": true,
|
||||
"plugin_type": "action_provider",
|
||||
"components": [
|
||||
{
|
||||
"type": "action",
|
||||
"name": "no_reply",
|
||||
"description": "暂时不回复消息,等待新消息或超时"
|
||||
},
|
||||
{
|
||||
"type": "action",
|
||||
"name": "emoji",
|
||||
"description": "发送表情包辅助表达情绪"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
159
src/plugins/built_in/core_actions/emoji.py
Normal file
159
src/plugins/built_in/core_actions/emoji.py
Normal file
@@ -0,0 +1,159 @@
|
||||
import random
|
||||
from typing import Tuple
|
||||
|
||||
# 导入新插件系统
|
||||
from src.plugin_system import BaseAction, ActionActivationType, ChatMode
|
||||
|
||||
# 导入依赖的系统组件
|
||||
from src.common.logger import get_logger
|
||||
|
||||
# 导入API模块 - 标准Python包方式
|
||||
from src.plugin_system.apis import emoji_api, llm_api, message_api
|
||||
# 注释:不再需要导入NoReplyAction,因为计数器管理已移至heartFC_chat.py
|
||||
# from src.plugins.built_in.core_actions.no_reply import NoReplyAction
|
||||
from src.config.config import global_config
|
||||
|
||||
|
||||
logger = get_logger("emoji")
|
||||
|
||||
|
||||
class EmojiAction(BaseAction):
|
||||
"""表情动作 - 发送表情包"""
|
||||
|
||||
# 激活设置
|
||||
if global_config.emoji.emoji_activate_type == "llm":
|
||||
activation_type = ActionActivationType.LLM_JUDGE
|
||||
random_activation_probability = 0
|
||||
else:
|
||||
activation_type = ActionActivationType.RANDOM
|
||||
random_activation_probability = global_config.emoji.emoji_chance
|
||||
mode_enable = ChatMode.ALL
|
||||
parallel_action = True
|
||||
|
||||
# 动作基本信息
|
||||
action_name = "emoji"
|
||||
action_description = "发送表情包辅助表达情绪"
|
||||
|
||||
# LLM判断提示词
|
||||
llm_judge_prompt = """
|
||||
判定是否需要使用表情动作的条件:
|
||||
1. 用户明确要求使用表情包
|
||||
2. 这是一个适合表达强烈情绪的场合
|
||||
3. 不要发送太多表情包,如果你已经发送过多个表情包则回答"否"
|
||||
|
||||
请回答"是"或"否"。
|
||||
"""
|
||||
|
||||
# 动作参数定义
|
||||
action_parameters = {}
|
||||
|
||||
# 动作使用场景
|
||||
action_require = [
|
||||
"发送表情包辅助表达情绪",
|
||||
"表达情绪时可以选择使用",
|
||||
"不要连续发送,如果你已经发过[表情包],就不要选择此动作",
|
||||
]
|
||||
|
||||
# 关联类型
|
||||
associated_types = ["emoji"]
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
# sourcery skip: assign-if-exp, introduce-default-else, swap-if-else-branches, use-named-expression
|
||||
"""执行表情动作"""
|
||||
logger.info(f"{self.log_prefix} 决定发送表情")
|
||||
|
||||
try:
|
||||
# 1. 获取发送表情的原因
|
||||
reason = self.action_data.get("reason", "表达当前情绪")
|
||||
logger.info(f"{self.log_prefix} 发送表情原因: {reason}")
|
||||
|
||||
# 2. 随机获取20个表情包
|
||||
sampled_emojis = await emoji_api.get_random(30)
|
||||
if not sampled_emojis:
|
||||
logger.warning(f"{self.log_prefix} 无法获取随机表情包")
|
||||
return False, "无法获取随机表情包"
|
||||
|
||||
# 3. 准备情感数据
|
||||
emotion_map = {}
|
||||
for b64, desc, emo in sampled_emojis:
|
||||
if emo not in emotion_map:
|
||||
emotion_map[emo] = []
|
||||
emotion_map[emo].append((b64, desc))
|
||||
|
||||
available_emotions = list(emotion_map.keys())
|
||||
|
||||
if not available_emotions:
|
||||
logger.warning(f"{self.log_prefix} 获取到的表情包均无情感标签, 将随机发送")
|
||||
emoji_base64, emoji_description, _ = random.choice(sampled_emojis)
|
||||
else:
|
||||
# 获取最近的5条消息内容用于判断
|
||||
recent_messages = message_api.get_recent_messages(chat_id=self.chat_id, limit=5)
|
||||
messages_text = ""
|
||||
if recent_messages:
|
||||
# 使用message_api构建可读的消息字符串
|
||||
messages_text = message_api.build_readable_messages(
|
||||
messages=recent_messages,
|
||||
timestamp_mode="normal_no_YMD",
|
||||
truncate=False,
|
||||
show_actions=False,
|
||||
)
|
||||
|
||||
# 4. 构建prompt让LLM选择情感
|
||||
prompt = f"""
|
||||
你是一个正在进行聊天的网友,你需要根据一个理由和最近的聊天记录,从一个情感标签列表中选择最匹配的一个。
|
||||
这是最近的聊天记录:
|
||||
{messages_text}
|
||||
|
||||
这是理由:“{reason}”
|
||||
这里是可用的情感标签:{available_emotions}
|
||||
请直接返回最匹配的那个情感标签,不要进行任何解释或添加其他多余的文字。
|
||||
"""
|
||||
|
||||
if global_config.debug.show_prompt:
|
||||
logger.info(f"{self.log_prefix} 生成的LLM Prompt: {prompt}")
|
||||
else:
|
||||
logger.debug(f"{self.log_prefix} 生成的LLM Prompt: {prompt}")
|
||||
|
||||
# 5. 调用LLM
|
||||
models = llm_api.get_available_models()
|
||||
chat_model_config = models.get("utils_small") # 使用字典访问方式
|
||||
if not chat_model_config:
|
||||
logger.error(f"{self.log_prefix} 未找到'utils_small'模型配置,无法调用LLM")
|
||||
return False, "未找到'utils_small'模型配置"
|
||||
|
||||
success, chosen_emotion, _, _ = await llm_api.generate_with_model(
|
||||
prompt, model_config=chat_model_config, request_type="emoji"
|
||||
)
|
||||
|
||||
if not success:
|
||||
logger.error(f"{self.log_prefix} LLM调用失败: {chosen_emotion}")
|
||||
return False, f"LLM调用失败: {chosen_emotion}"
|
||||
|
||||
chosen_emotion = chosen_emotion.strip().replace('"', "").replace("'", "")
|
||||
logger.info(f"{self.log_prefix} LLM选择的情感: {chosen_emotion}")
|
||||
|
||||
# 6. 根据选择的情感匹配表情包
|
||||
if chosen_emotion in emotion_map:
|
||||
emoji_base64, emoji_description = random.choice(emotion_map[chosen_emotion])
|
||||
logger.info(f"{self.log_prefix} 找到匹配情感 '{chosen_emotion}' 的表情包: {emoji_description}")
|
||||
else:
|
||||
logger.warning(
|
||||
f"{self.log_prefix} LLM选择的情感 '{chosen_emotion}' 不在可用列表中, 将随机选择一个表情包"
|
||||
)
|
||||
emoji_base64, emoji_description, _ = random.choice(sampled_emojis)
|
||||
|
||||
# 7. 发送表情包
|
||||
success = await self.send_emoji(emoji_base64)
|
||||
|
||||
if not success:
|
||||
logger.error(f"{self.log_prefix} 表情包发送失败")
|
||||
return False, "表情包发送失败"
|
||||
|
||||
# 注释:重置NoReplyAction的连续计数器现在由heartFC_chat.py统一管理
|
||||
# NoReplyAction.reset_consecutive_count()
|
||||
|
||||
return True, f"发送表情包: {emoji_description}"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 表情动作执行失败: {e}", exc_info=True)
|
||||
return False, f"表情发送失败: {str(e)}"
|
||||
89
src/plugins/built_in/core_actions/no_reply.py
Normal file
89
src/plugins/built_in/core_actions/no_reply.py
Normal file
@@ -0,0 +1,89 @@
|
||||
from typing import Tuple, List
|
||||
from collections import deque
|
||||
|
||||
# 导入新插件系统
|
||||
from src.plugin_system import BaseAction, ActionActivationType, ChatMode
|
||||
|
||||
# 导入依赖的系统组件
|
||||
from src.common.logger import get_logger
|
||||
|
||||
|
||||
logger = get_logger("no_reply_action")
|
||||
|
||||
|
||||
class NoReplyAction(BaseAction):
|
||||
"""不回复动作,支持waiting和breaking两种形式.
|
||||
|
||||
waiting形式:
|
||||
- 只要有新消息就结束动作
|
||||
- 记录新消息的兴趣度到列表(最多保留最近三项)
|
||||
- 如果最近三次动作都是no_reply,且最近新消息列表兴趣度之和小于阈值,就进入breaking形式
|
||||
|
||||
breaking形式:
|
||||
- 和原有逻辑一致,需要消息满足一定数量或累计一定兴趣值才结束动作
|
||||
"""
|
||||
|
||||
focus_activation_type = ActionActivationType.NEVER
|
||||
normal_activation_type = ActionActivationType.NEVER
|
||||
mode_enable = ChatMode.FOCUS
|
||||
parallel_action = False
|
||||
|
||||
# 动作基本信息
|
||||
action_name = "no_reply"
|
||||
action_description = "暂时不回复消息"
|
||||
|
||||
# 最近三次no_reply的新消息兴趣度记录
|
||||
_recent_interest_records: deque = deque(maxlen=3)
|
||||
|
||||
# 兴趣值退出阈值
|
||||
_interest_exit_threshold = 3.0
|
||||
# 消息数量退出阈值
|
||||
_min_exit_message_count = 3
|
||||
_max_exit_message_count = 6
|
||||
|
||||
# 动作参数定义
|
||||
action_parameters = {}
|
||||
|
||||
# 动作使用场景
|
||||
action_require = [""]
|
||||
|
||||
# 关联类型
|
||||
associated_types = []
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
"""执行不回复动作"""
|
||||
|
||||
try:
|
||||
reason = self.action_data.get("reason", "")
|
||||
|
||||
logger.info(f"{self.log_prefix} 选择不回复,原因: {reason}")
|
||||
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=False,
|
||||
action_prompt_display=reason,
|
||||
action_done=True,
|
||||
)
|
||||
return True, reason
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 不回复动作执行失败: {e}")
|
||||
exit_reason = f"执行异常: {str(e)}"
|
||||
full_prompt = f"no_reply执行异常: {exit_reason},你思考是否要进行回复"
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=full_prompt,
|
||||
action_done=True,
|
||||
)
|
||||
return False, f"不回复动作执行失败: {e}"
|
||||
|
||||
@classmethod
|
||||
def reset_consecutive_count(cls):
|
||||
"""重置连续计数器和兴趣度记录"""
|
||||
cls._recent_interest_records.clear()
|
||||
logger.debug("NoReplyAction连续计数器和兴趣度记录已重置")
|
||||
|
||||
@classmethod
|
||||
def get_recent_interest_records(cls) -> List[float]:
|
||||
"""获取最近的兴趣度记录"""
|
||||
return list(cls._recent_interest_records)
|
||||
|
||||
72
src/plugins/built_in/core_actions/plugin.py
Normal file
72
src/plugins/built_in/core_actions/plugin.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
核心动作插件
|
||||
|
||||
将系统核心动作(reply、no_reply、emoji)转换为新插件系统格式
|
||||
这是系统的内置插件,提供基础的聊天交互功能
|
||||
"""
|
||||
|
||||
from typing import List, Tuple, Type
|
||||
|
||||
# 导入新插件系统
|
||||
from src.plugin_system import BasePlugin, register_plugin, ComponentInfo
|
||||
from src.plugin_system.base.config_types import ConfigField
|
||||
|
||||
# 导入依赖的系统组件
|
||||
from src.common.logger import get_logger
|
||||
|
||||
# 导入API模块 - 标准Python包方式
|
||||
from src.plugins.built_in.core_actions.no_reply import NoReplyAction
|
||||
from src.plugins.built_in.core_actions.emoji import EmojiAction
|
||||
|
||||
logger = get_logger("core_actions")
|
||||
|
||||
|
||||
@register_plugin
|
||||
class CoreActionsPlugin(BasePlugin):
|
||||
"""核心动作插件
|
||||
|
||||
系统内置插件,提供基础的聊天交互功能:
|
||||
- Reply: 回复动作
|
||||
- NoReply: 不回复动作
|
||||
- Emoji: 表情动作
|
||||
|
||||
注意:插件基本信息优先从_manifest.json文件中读取
|
||||
"""
|
||||
|
||||
# 插件基本信息
|
||||
plugin_name: str = "core_actions" # 内部标识符
|
||||
enable_plugin: bool = True
|
||||
dependencies: list[str] = [] # 插件依赖列表
|
||||
python_dependencies: list[str] = [] # Python包依赖列表
|
||||
config_file_name: str = "config.toml"
|
||||
|
||||
# 配置节描述
|
||||
config_section_descriptions = {
|
||||
"plugin": "插件启用配置",
|
||||
"components": "核心组件启用配置",
|
||||
}
|
||||
|
||||
# 配置Schema定义
|
||||
config_schema: dict = {
|
||||
"plugin": {
|
||||
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
|
||||
"config_version": ConfigField(type=str, default="0.5.0", description="配置文件版本"),
|
||||
},
|
||||
"components": {
|
||||
"enable_no_reply": ConfigField(type=bool, default=True, description="是否启用不回复动作"),
|
||||
"enable_emoji": ConfigField(type=bool, default=True, description="是否启用发送表情/图片动作"),
|
||||
},
|
||||
}
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
"""返回插件包含的组件列表"""
|
||||
|
||||
# --- 根据配置注册组件 ---
|
||||
components = []
|
||||
if self.get_config("components.enable_no_reply", True):
|
||||
components.append((NoReplyAction.get_action_info(), NoReplyAction))
|
||||
if self.get_config("components.enable_emoji", True):
|
||||
components.append((EmojiAction.get_action_info(), EmojiAction))
|
||||
|
||||
|
||||
return components
|
||||
56
src/plugins/built_in/knowledge/lpmm_get_knowledge.py
Normal file
56
src/plugins/built_in/knowledge/lpmm_get_knowledge.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from typing import Dict, Any
|
||||
|
||||
from src.common.logger import get_logger
|
||||
from src.config.config import global_config
|
||||
from src.chat.knowledge.knowledge_lib import qa_manager
|
||||
from src.plugin_system import BaseTool, ToolParamType
|
||||
|
||||
logger = get_logger("lpmm_get_knowledge_tool")
|
||||
|
||||
|
||||
class SearchKnowledgeFromLPMMTool(BaseTool):
|
||||
"""从LPMM知识库中搜索相关信息的工具"""
|
||||
|
||||
name = "lpmm_search_knowledge"
|
||||
description = "从知识库中搜索相关信息,如果你需要知识,就使用这个工具"
|
||||
parameters = [
|
||||
("query", ToolParamType.STRING, "搜索查询关键词", True, None),
|
||||
("threshold", ToolParamType.FLOAT, "相似度阈值,0.0到1.0之间", False, None),
|
||||
]
|
||||
available_for_llm = global_config.lpmm_knowledge.enable
|
||||
|
||||
async def execute(self, function_args: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""执行知识库搜索
|
||||
|
||||
Args:
|
||||
function_args: 工具参数
|
||||
|
||||
Returns:
|
||||
Dict: 工具执行结果
|
||||
"""
|
||||
try:
|
||||
query: str = function_args.get("query") # type: ignore
|
||||
# threshold = function_args.get("threshold", 0.4)
|
||||
|
||||
# 检查LPMM知识库是否启用
|
||||
if qa_manager is None:
|
||||
logger.debug("LPMM知识库已禁用,跳过知识获取")
|
||||
return {"type": "info", "id": query, "content": "LPMM知识库已禁用"}
|
||||
|
||||
# 调用知识库搜索
|
||||
|
||||
knowledge_info = await qa_manager.get_knowledge(query)
|
||||
|
||||
logger.debug(f"知识库查询结果: {knowledge_info}")
|
||||
|
||||
if knowledge_info:
|
||||
content = f"你知道这些知识: {knowledge_info}"
|
||||
else:
|
||||
content = f"你不太了解有关{query}的知识"
|
||||
return {"type": "lpmm_knowledge", "id": query, "content": content}
|
||||
except Exception as e:
|
||||
# 捕获异常并记录错误
|
||||
logger.error(f"知识库搜索工具执行失败: {str(e)}")
|
||||
# 在其他异常情况下,确保 id 仍然是 query (如果它被定义了)
|
||||
query_id = query if "query" in locals() else "unknown_query"
|
||||
return {"type": "info", "id": query_id, "content": f"lpmm知识库搜索失败,炸了: {str(e)}"}
|
||||
39
src/plugins/built_in/plugin_management/_manifest.json
Normal file
39
src/plugins/built_in/plugin_management/_manifest.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "插件和组件管理 (Plugin and Component Management)",
|
||||
"version": "1.0.0",
|
||||
"description": "通过系统API管理插件和组件的生命周期,包括加载、卸载、启用和禁用等操作。",
|
||||
"author": {
|
||||
"name": "MaiBot团队",
|
||||
"url": "https://github.com/MaiM-with-u"
|
||||
},
|
||||
"license": "GPL-v3.0-or-later",
|
||||
"host_application": {
|
||||
"min_version": "0.9.1"
|
||||
},
|
||||
"homepage_url": "https://github.com/MaiM-with-u/maibot",
|
||||
"repository_url": "https://github.com/MaiM-with-u/maibot",
|
||||
"keywords": [
|
||||
"plugins",
|
||||
"components",
|
||||
"management",
|
||||
"built-in"
|
||||
],
|
||||
"categories": [
|
||||
"Core System",
|
||||
"Plugin Management"
|
||||
],
|
||||
"default_locale": "zh-CN",
|
||||
"locales_path": "_locales",
|
||||
"plugin_info": {
|
||||
"is_built_in": true,
|
||||
"plugin_type": "plugin_management",
|
||||
"components": [
|
||||
{
|
||||
"type": "command",
|
||||
"name": "plugin_management",
|
||||
"description": "管理插件和组件的生命周期,包括加载、卸载、启用和禁用等操作。"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
454
src/plugins/built_in/plugin_management/plugin.py
Normal file
454
src/plugins/built_in/plugin_management/plugin.py
Normal file
@@ -0,0 +1,454 @@
|
||||
import asyncio
|
||||
|
||||
from typing import List, Tuple, Type
|
||||
from src.plugin_system import (
|
||||
BasePlugin,
|
||||
BaseCommand,
|
||||
CommandInfo,
|
||||
ConfigField,
|
||||
register_plugin,
|
||||
plugin_manage_api,
|
||||
component_manage_api,
|
||||
ComponentInfo,
|
||||
ComponentType,
|
||||
send_api,
|
||||
)
|
||||
|
||||
|
||||
class ManagementCommand(BaseCommand):
|
||||
command_name: str = "management"
|
||||
description: str = "管理命令"
|
||||
command_pattern: str = r"(?P<manage_command>^/pm(\s[a-zA-Z0-9_]+)*\s*$)"
|
||||
|
||||
async def execute(self) -> Tuple[bool, str, bool]:
|
||||
# sourcery skip: merge-duplicate-blocks
|
||||
if (
|
||||
not self.message
|
||||
or not self.message.message_info
|
||||
or not self.message.message_info.user_info
|
||||
or str(self.message.message_info.user_info.user_id) not in self.get_config("plugin.permission", []) # type: ignore
|
||||
):
|
||||
await self._send_message("你没有权限使用插件管理命令")
|
||||
return False, "没有权限", True
|
||||
if not self.message.chat_stream:
|
||||
await self._send_message("无法获取聊天流信息")
|
||||
return False, "无法获取聊天流信息", True
|
||||
self.stream_id = self.message.chat_stream.stream_id
|
||||
if not self.stream_id:
|
||||
await self._send_message("无法获取聊天流信息")
|
||||
return False, "无法获取聊天流信息", True
|
||||
command_list = self.matched_groups["manage_command"].strip().split(" ")
|
||||
if len(command_list) == 1:
|
||||
await self.show_help("all")
|
||||
return True, "帮助已发送", True
|
||||
if len(command_list) == 2:
|
||||
match command_list[1]:
|
||||
case "plugin":
|
||||
await self.show_help("plugin")
|
||||
case "component":
|
||||
await self.show_help("component")
|
||||
case "help":
|
||||
await self.show_help("all")
|
||||
case _:
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
if len(command_list) == 3:
|
||||
if command_list[1] == "plugin":
|
||||
match command_list[2]:
|
||||
case "help":
|
||||
await self.show_help("plugin")
|
||||
case "list":
|
||||
await self._list_registered_plugins()
|
||||
case "list_enabled":
|
||||
await self._list_loaded_plugins()
|
||||
case "rescan":
|
||||
await self._rescan_plugin_dirs()
|
||||
case _:
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
elif command_list[1] == "component":
|
||||
if command_list[2] == "list":
|
||||
await self._list_all_registered_components()
|
||||
elif command_list[2] == "help":
|
||||
await self.show_help("component")
|
||||
else:
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
else:
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
if len(command_list) == 4:
|
||||
if command_list[1] == "plugin":
|
||||
match command_list[2]:
|
||||
case "load":
|
||||
await self._load_plugin(command_list[3])
|
||||
case "unload":
|
||||
await self._unload_plugin(command_list[3])
|
||||
case "reload":
|
||||
await self._reload_plugin(command_list[3])
|
||||
case "add_dir":
|
||||
await self._add_dir(command_list[3])
|
||||
case _:
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
elif command_list[1] == "component":
|
||||
if command_list[2] != "list":
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
if command_list[3] == "enabled":
|
||||
await self._list_enabled_components()
|
||||
elif command_list[3] == "disabled":
|
||||
await self._list_disabled_components()
|
||||
else:
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
else:
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
if len(command_list) == 5:
|
||||
if command_list[1] != "component":
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
if command_list[2] != "list":
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
if command_list[3] == "enabled":
|
||||
await self._list_enabled_components(target_type=command_list[4])
|
||||
elif command_list[3] == "disabled":
|
||||
await self._list_disabled_components(target_type=command_list[4])
|
||||
elif command_list[3] == "type":
|
||||
await self._list_registered_components_by_type(command_list[4])
|
||||
else:
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
if len(command_list) == 6:
|
||||
if command_list[1] != "component":
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
if command_list[2] == "enable":
|
||||
if command_list[3] == "global":
|
||||
await self._globally_enable_component(command_list[4], command_list[5])
|
||||
elif command_list[3] == "local":
|
||||
await self._locally_enable_component(command_list[4], command_list[5])
|
||||
else:
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
elif command_list[2] == "disable":
|
||||
if command_list[3] == "global":
|
||||
await self._globally_disable_component(command_list[4], command_list[5])
|
||||
elif command_list[3] == "local":
|
||||
await self._locally_disable_component(command_list[4], command_list[5])
|
||||
else:
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
else:
|
||||
await self._send_message("插件管理命令不合法")
|
||||
return False, "命令不合法", True
|
||||
|
||||
return True, "命令执行完成", True
|
||||
|
||||
async def show_help(self, target: str):
|
||||
help_msg = ""
|
||||
match target:
|
||||
case "all":
|
||||
help_msg = (
|
||||
"管理命令帮助\n"
|
||||
"/pm help 管理命令提示\n"
|
||||
"/pm plugin 插件管理命令\n"
|
||||
"/pm component 组件管理命令\n"
|
||||
"使用 /pm plugin help 或 /pm component help 获取具体帮助"
|
||||
)
|
||||
case "plugin":
|
||||
help_msg = (
|
||||
"插件管理命令帮助\n"
|
||||
"/pm plugin help 插件管理命令提示\n"
|
||||
"/pm plugin list 列出所有注册的插件\n"
|
||||
"/pm plugin list_enabled 列出所有加载(启用)的插件\n"
|
||||
"/pm plugin rescan 重新扫描所有目录\n"
|
||||
"/pm plugin load <plugin_name> 加载指定插件\n"
|
||||
"/pm plugin unload <plugin_name> 卸载指定插件\n"
|
||||
"/pm plugin reload <plugin_name> 重新加载指定插件\n"
|
||||
"/pm plugin add_dir <directory_path> 添加插件目录\n"
|
||||
)
|
||||
case "component":
|
||||
help_msg = (
|
||||
"组件管理命令帮助\n"
|
||||
"/pm component help 组件管理命令提示\n"
|
||||
"/pm component list 列出所有注册的组件\n"
|
||||
"/pm component list enabled <可选: type> 列出所有启用的组件\n"
|
||||
"/pm component list disabled <可选: type> 列出所有禁用的组件\n"
|
||||
" - <type> 可选项: local,代表当前聊天中的;global,代表全局的\n"
|
||||
" - <type> 不填时为 global\n"
|
||||
"/pm component list type <component_type> 列出已经注册的指定类型的组件\n"
|
||||
"/pm component enable global <component_name> <component_type> 全局启用组件\n"
|
||||
"/pm component enable local <component_name> <component_type> 本聊天启用组件\n"
|
||||
"/pm component disable global <component_name> <component_type> 全局禁用组件\n"
|
||||
"/pm component disable local <component_name> <component_type> 本聊天禁用组件\n"
|
||||
" - <component_type> 可选项: action, command, event_handler\n"
|
||||
)
|
||||
case _:
|
||||
return
|
||||
await self._send_message(help_msg)
|
||||
|
||||
async def _list_loaded_plugins(self):
|
||||
plugins = plugin_manage_api.list_loaded_plugins()
|
||||
await self._send_message(f"已加载的插件: {', '.join(plugins)}")
|
||||
|
||||
async def _list_registered_plugins(self):
|
||||
plugins = plugin_manage_api.list_registered_plugins()
|
||||
await self._send_message(f"已注册的插件: {', '.join(plugins)}")
|
||||
|
||||
async def _rescan_plugin_dirs(self):
|
||||
plugin_manage_api.rescan_plugin_directory()
|
||||
await self._send_message("插件目录重新扫描执行中")
|
||||
|
||||
async def _load_plugin(self, plugin_name: str):
|
||||
success, count = plugin_manage_api.load_plugin(plugin_name)
|
||||
if success:
|
||||
await self._send_message(f"插件加载成功: {plugin_name}")
|
||||
else:
|
||||
if count == 0:
|
||||
await self._send_message(f"插件{plugin_name}为禁用状态")
|
||||
await self._send_message(f"插件加载失败: {plugin_name}")
|
||||
|
||||
async def _unload_plugin(self, plugin_name: str):
|
||||
success = await plugin_manage_api.remove_plugin(plugin_name)
|
||||
if success:
|
||||
await self._send_message(f"插件卸载成功: {plugin_name}")
|
||||
else:
|
||||
await self._send_message(f"插件卸载失败: {plugin_name}")
|
||||
|
||||
async def _reload_plugin(self, plugin_name: str):
|
||||
success = await plugin_manage_api.reload_plugin(plugin_name)
|
||||
if success:
|
||||
await self._send_message(f"插件重新加载成功: {plugin_name}")
|
||||
else:
|
||||
await self._send_message(f"插件重新加载失败: {plugin_name}")
|
||||
|
||||
async def _add_dir(self, dir_path: str):
|
||||
await self._send_message(f"正在添加插件目录: {dir_path}")
|
||||
success = plugin_manage_api.add_plugin_directory(dir_path)
|
||||
await asyncio.sleep(0.5) # 防止乱序发送
|
||||
if success:
|
||||
await self._send_message(f"插件目录添加成功: {dir_path}")
|
||||
else:
|
||||
await self._send_message(f"插件目录添加失败: {dir_path}")
|
||||
|
||||
def _fetch_all_registered_components(self) -> List[ComponentInfo]:
|
||||
all_plugin_info = component_manage_api.get_all_plugin_info()
|
||||
if not all_plugin_info:
|
||||
return []
|
||||
|
||||
components_info: List[ComponentInfo] = []
|
||||
for plugin_info in all_plugin_info.values():
|
||||
components_info.extend(plugin_info.components)
|
||||
return components_info
|
||||
|
||||
def _fetch_locally_disabled_components(self) -> List[str]:
|
||||
locally_disabled_components_actions = component_manage_api.get_locally_disabled_components(
|
||||
self.message.chat_stream.stream_id, ComponentType.ACTION
|
||||
)
|
||||
locally_disabled_components_commands = component_manage_api.get_locally_disabled_components(
|
||||
self.message.chat_stream.stream_id, ComponentType.COMMAND
|
||||
)
|
||||
locally_disabled_components_event_handlers = component_manage_api.get_locally_disabled_components(
|
||||
self.message.chat_stream.stream_id, ComponentType.EVENT_HANDLER
|
||||
)
|
||||
return (
|
||||
locally_disabled_components_actions
|
||||
+ locally_disabled_components_commands
|
||||
+ locally_disabled_components_event_handlers
|
||||
)
|
||||
|
||||
async def _list_all_registered_components(self):
|
||||
components_info = self._fetch_all_registered_components()
|
||||
if not components_info:
|
||||
await self._send_message("没有注册的组件")
|
||||
return
|
||||
|
||||
all_components_str = ", ".join(
|
||||
f"{component.name} ({component.component_type})" for component in components_info
|
||||
)
|
||||
await self._send_message(f"已注册的组件: {all_components_str}")
|
||||
|
||||
async def _list_enabled_components(self, target_type: str = "global"):
|
||||
components_info = self._fetch_all_registered_components()
|
||||
if not components_info:
|
||||
await self._send_message("没有注册的组件")
|
||||
return
|
||||
|
||||
if target_type == "global":
|
||||
enabled_components = [component for component in components_info if component.enabled]
|
||||
if not enabled_components:
|
||||
await self._send_message("没有满足条件的已启用全局组件")
|
||||
return
|
||||
enabled_components_str = ", ".join(
|
||||
f"{component.name} ({component.component_type})" for component in enabled_components
|
||||
)
|
||||
await self._send_message(f"满足条件的已启用全局组件: {enabled_components_str}")
|
||||
elif target_type == "local":
|
||||
locally_disabled_components = self._fetch_locally_disabled_components()
|
||||
enabled_components = [
|
||||
component
|
||||
for component in components_info
|
||||
if (component.name not in locally_disabled_components and component.enabled)
|
||||
]
|
||||
if not enabled_components:
|
||||
await self._send_message("本聊天没有满足条件的已启用组件")
|
||||
return
|
||||
enabled_components_str = ", ".join(
|
||||
f"{component.name} ({component.component_type})" for component in enabled_components
|
||||
)
|
||||
await self._send_message(f"本聊天满足条件的已启用组件: {enabled_components_str}")
|
||||
|
||||
async def _list_disabled_components(self, target_type: str = "global"):
|
||||
components_info = self._fetch_all_registered_components()
|
||||
if not components_info:
|
||||
await self._send_message("没有注册的组件")
|
||||
return
|
||||
|
||||
if target_type == "global":
|
||||
disabled_components = [component for component in components_info if not component.enabled]
|
||||
if not disabled_components:
|
||||
await self._send_message("没有满足条件的已禁用全局组件")
|
||||
return
|
||||
disabled_components_str = ", ".join(
|
||||
f"{component.name} ({component.component_type})" for component in disabled_components
|
||||
)
|
||||
await self._send_message(f"满足条件的已禁用全局组件: {disabled_components_str}")
|
||||
elif target_type == "local":
|
||||
locally_disabled_components = self._fetch_locally_disabled_components()
|
||||
disabled_components = [
|
||||
component
|
||||
for component in components_info
|
||||
if (component.name in locally_disabled_components or not component.enabled)
|
||||
]
|
||||
if not disabled_components:
|
||||
await self._send_message("本聊天没有满足条件的已禁用组件")
|
||||
return
|
||||
disabled_components_str = ", ".join(
|
||||
f"{component.name} ({component.component_type})" for component in disabled_components
|
||||
)
|
||||
await self._send_message(f"本聊天满足条件的已禁用组件: {disabled_components_str}")
|
||||
|
||||
async def _list_registered_components_by_type(self, target_type: str):
|
||||
match target_type:
|
||||
case "action":
|
||||
component_type = ComponentType.ACTION
|
||||
case "command":
|
||||
component_type = ComponentType.COMMAND
|
||||
case "event_handler":
|
||||
component_type = ComponentType.EVENT_HANDLER
|
||||
case _:
|
||||
await self._send_message(f"未知组件类型: {target_type}")
|
||||
return
|
||||
|
||||
components_info = component_manage_api.get_components_info_by_type(component_type)
|
||||
if not components_info:
|
||||
await self._send_message(f"没有注册的 {target_type} 组件")
|
||||
return
|
||||
|
||||
components_str = ", ".join(
|
||||
f"{name} ({component.component_type})" for name, component in components_info.items()
|
||||
)
|
||||
await self._send_message(f"注册的 {target_type} 组件: {components_str}")
|
||||
|
||||
async def _globally_enable_component(self, component_name: str, component_type: str):
|
||||
match component_type:
|
||||
case "action":
|
||||
target_component_type = ComponentType.ACTION
|
||||
case "command":
|
||||
target_component_type = ComponentType.COMMAND
|
||||
case "event_handler":
|
||||
target_component_type = ComponentType.EVENT_HANDLER
|
||||
case _:
|
||||
await self._send_message(f"未知组件类型: {component_type}")
|
||||
return
|
||||
if component_manage_api.globally_enable_component(component_name, target_component_type):
|
||||
await self._send_message(f"全局启用组件成功: {component_name}")
|
||||
else:
|
||||
await self._send_message(f"全局启用组件失败: {component_name}")
|
||||
|
||||
async def _globally_disable_component(self, component_name: str, component_type: str):
|
||||
match component_type:
|
||||
case "action":
|
||||
target_component_type = ComponentType.ACTION
|
||||
case "command":
|
||||
target_component_type = ComponentType.COMMAND
|
||||
case "event_handler":
|
||||
target_component_type = ComponentType.EVENT_HANDLER
|
||||
case _:
|
||||
await self._send_message(f"未知组件类型: {component_type}")
|
||||
return
|
||||
success = await component_manage_api.globally_disable_component(component_name, target_component_type)
|
||||
if success:
|
||||
await self._send_message(f"全局禁用组件成功: {component_name}")
|
||||
else:
|
||||
await self._send_message(f"全局禁用组件失败: {component_name}")
|
||||
|
||||
async def _locally_enable_component(self, component_name: str, component_type: str):
|
||||
match component_type:
|
||||
case "action":
|
||||
target_component_type = ComponentType.ACTION
|
||||
case "command":
|
||||
target_component_type = ComponentType.COMMAND
|
||||
case "event_handler":
|
||||
target_component_type = ComponentType.EVENT_HANDLER
|
||||
case _:
|
||||
await self._send_message(f"未知组件类型: {component_type}")
|
||||
return
|
||||
if component_manage_api.locally_enable_component(
|
||||
component_name,
|
||||
target_component_type,
|
||||
self.message.chat_stream.stream_id,
|
||||
):
|
||||
await self._send_message(f"本地启用组件成功: {component_name}")
|
||||
else:
|
||||
await self._send_message(f"本地启用组件失败: {component_name}")
|
||||
|
||||
async def _locally_disable_component(self, component_name: str, component_type: str):
|
||||
match component_type:
|
||||
case "action":
|
||||
target_component_type = ComponentType.ACTION
|
||||
case "command":
|
||||
target_component_type = ComponentType.COMMAND
|
||||
case "event_handler":
|
||||
target_component_type = ComponentType.EVENT_HANDLER
|
||||
case _:
|
||||
await self._send_message(f"未知组件类型: {component_type}")
|
||||
return
|
||||
if component_manage_api.locally_disable_component(
|
||||
component_name,
|
||||
target_component_type,
|
||||
self.message.chat_stream.stream_id,
|
||||
):
|
||||
await self._send_message(f"本地禁用组件成功: {component_name}")
|
||||
else:
|
||||
await self._send_message(f"本地禁用组件失败: {component_name}")
|
||||
|
||||
async def _send_message(self, message: str):
|
||||
await send_api.text_to_stream(message, self.stream_id, typing=False, storage_message=False)
|
||||
|
||||
|
||||
@register_plugin
|
||||
class PluginManagementPlugin(BasePlugin):
|
||||
plugin_name: str = "plugin_management_plugin"
|
||||
enable_plugin: bool = False
|
||||
dependencies: list[str] = []
|
||||
python_dependencies: list[str] = []
|
||||
config_file_name: str = "config.toml"
|
||||
config_schema: dict = {
|
||||
"plugin": {
|
||||
"enabled": ConfigField(bool, default=False, description="是否启用插件"),
|
||||
"config_version": ConfigField(type=str, default="1.1.0", description="配置文件版本"),
|
||||
"permission": ConfigField(
|
||||
list, default=[], description="有权限使用插件管理命令的用户列表,请填写字符串形式的用户ID"
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[CommandInfo, Type[BaseCommand]]]:
|
||||
components = []
|
||||
if self.get_config("plugin.enabled", True):
|
||||
components.append((ManagementCommand.get_command_info(), ManagementCommand))
|
||||
return components
|
||||
42
src/plugins/built_in/tts_plugin/_manifest.json
Normal file
42
src/plugins/built_in/tts_plugin/_manifest.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "文本转语音插件 (Text-to-Speech)",
|
||||
"version": "0.1.0",
|
||||
"description": "将文本转换为语音进行播放的插件,支持多种语音模式和智能语音输出场景判断。",
|
||||
"author": {
|
||||
"name": "MaiBot团队",
|
||||
"url": "https://github.com/MaiM-with-u"
|
||||
},
|
||||
"license": "GPL-v3.0-or-later",
|
||||
|
||||
"host_application": {
|
||||
"min_version": "0.8.0"
|
||||
},
|
||||
"homepage_url": "https://github.com/MaiM-with-u/maibot",
|
||||
"repository_url": "https://github.com/MaiM-with-u/maibot",
|
||||
"keywords": ["tts", "voice", "audio", "speech", "accessibility"],
|
||||
"categories": ["Audio Tools", "Accessibility", "Voice Assistant"],
|
||||
|
||||
"default_locale": "zh-CN",
|
||||
"locales_path": "_locales",
|
||||
|
||||
"plugin_info": {
|
||||
"is_built_in": true,
|
||||
"plugin_type": "audio_processor",
|
||||
"components": [
|
||||
{
|
||||
"type": "action",
|
||||
"name": "tts_action",
|
||||
"description": "将文本转换为语音进行播放",
|
||||
"activation_modes": ["llm_judge", "keyword"],
|
||||
"keywords": ["语音", "tts", "播报", "读出来", "语音播放", "听", "朗读"]
|
||||
}
|
||||
],
|
||||
"features": [
|
||||
"文本转语音播放",
|
||||
"智能场景判断",
|
||||
"关键词触发",
|
||||
"支持多种语音模式"
|
||||
]
|
||||
}
|
||||
}
|
||||
149
src/plugins/built_in/tts_plugin/plugin.py
Normal file
149
src/plugins/built_in/tts_plugin/plugin.py
Normal file
@@ -0,0 +1,149 @@
|
||||
from src.plugin_system.apis.plugin_register_api import register_plugin
|
||||
from src.plugin_system.base.base_plugin import BasePlugin
|
||||
from src.plugin_system.base.component_types import ComponentInfo
|
||||
from src.common.logger import get_logger
|
||||
from src.plugin_system.base.base_action import BaseAction, ActionActivationType, ChatMode
|
||||
from src.plugin_system.base.config_types import ConfigField
|
||||
from typing import Tuple, List, Type
|
||||
|
||||
logger = get_logger("tts")
|
||||
|
||||
|
||||
class TTSAction(BaseAction):
|
||||
"""TTS语音转换动作处理类"""
|
||||
|
||||
# 激活设置
|
||||
focus_activation_type = ActionActivationType.LLM_JUDGE
|
||||
normal_activation_type = ActionActivationType.KEYWORD
|
||||
mode_enable = ChatMode.ALL
|
||||
parallel_action = False
|
||||
|
||||
# 动作基本信息
|
||||
action_name = "tts_action"
|
||||
action_description = "将文本转换为语音进行播放,适用于需要语音输出的场景"
|
||||
|
||||
# 关键词配置 - Normal模式下使用关键词触发
|
||||
activation_keywords = ["语音", "tts", "播报", "读出来", "语音播放", "听", "朗读"]
|
||||
keyword_case_sensitive = False
|
||||
|
||||
# 动作参数定义
|
||||
action_parameters = {
|
||||
"text": "需要转换为语音的文本内容,必填,内容应当适合语音播报,语句流畅、清晰",
|
||||
}
|
||||
|
||||
# 动作使用场景
|
||||
action_require = [
|
||||
"当需要发送语音信息时使用",
|
||||
"当用户明确要求使用语音功能时使用",
|
||||
"当表达内容更适合用语音而不是文字传达时使用",
|
||||
"当用户想听到语音回答而非阅读文本时使用",
|
||||
]
|
||||
|
||||
# 关联类型
|
||||
associated_types = ["tts_text"]
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
"""处理TTS文本转语音动作"""
|
||||
logger.info(f"{self.log_prefix} 执行TTS动作: {self.reasoning}")
|
||||
|
||||
# 获取要转换的文本
|
||||
text = self.action_data.get("text")
|
||||
|
||||
if not text:
|
||||
logger.error(f"{self.log_prefix} 执行TTS动作时未提供文本内容")
|
||||
return False, "执行TTS动作失败:未提供文本内容"
|
||||
|
||||
# 确保文本适合TTS使用
|
||||
processed_text = self._process_text_for_tts(text)
|
||||
|
||||
try:
|
||||
# 发送TTS消息
|
||||
await self.send_custom(message_type="tts_text", content=processed_text)
|
||||
|
||||
# 记录动作信息
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True, action_prompt_display="已经发送了语音消息。", action_done=True
|
||||
)
|
||||
|
||||
logger.info(f"{self.log_prefix} TTS动作执行成功,文本长度: {len(processed_text)}")
|
||||
return True, "TTS动作执行成功"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 执行TTS动作时出错: {e}")
|
||||
return False, f"执行TTS动作时出错: {e}"
|
||||
|
||||
def _process_text_for_tts(self, text: str) -> str:
|
||||
"""
|
||||
处理文本使其更适合TTS使用
|
||||
- 移除不必要的特殊字符和表情符号
|
||||
- 修正标点符号以提高语音质量
|
||||
- 优化文本结构使语音更流畅
|
||||
"""
|
||||
# 这里可以添加文本处理逻辑
|
||||
# 例如:移除多余的标点、表情符号,优化语句结构等
|
||||
|
||||
# 简单示例实现
|
||||
processed_text = text
|
||||
|
||||
# 移除多余的标点符号
|
||||
import re
|
||||
|
||||
processed_text = re.sub(r"([!?,.;:。!?,、;:])\1+", r"\1", processed_text)
|
||||
|
||||
# 确保句子结尾有合适的标点
|
||||
if not any(processed_text.endswith(end) for end in [".", "?", "!", "。", "!", "?"]):
|
||||
processed_text = f"{processed_text}。"
|
||||
|
||||
return processed_text
|
||||
|
||||
|
||||
@register_plugin
|
||||
class TTSPlugin(BasePlugin):
|
||||
"""TTS插件
|
||||
- 这是文字转语音插件
|
||||
- Normal模式下依靠关键词触发
|
||||
- Focus模式下由LLM判断触发
|
||||
- 具有一定的文本预处理能力
|
||||
"""
|
||||
|
||||
# 插件基本信息
|
||||
plugin_name: str = "tts_plugin" # 内部标识符
|
||||
enable_plugin: bool = True
|
||||
dependencies: list[str] = [] # 插件依赖列表
|
||||
python_dependencies: list[str] = [] # Python包依赖列表
|
||||
config_file_name: str = "config.toml"
|
||||
|
||||
# 配置节描述
|
||||
config_section_descriptions = {
|
||||
"plugin": "插件基本信息配置",
|
||||
"components": "组件启用控制",
|
||||
"logging": "日志记录相关配置",
|
||||
}
|
||||
|
||||
# 配置Schema定义
|
||||
config_schema: dict = {
|
||||
"plugin": {
|
||||
"name": ConfigField(type=str, default="tts_plugin", description="插件名称", required=True),
|
||||
"version": ConfigField(type=str, default="0.1.0", description="插件版本号"),
|
||||
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
|
||||
"description": ConfigField(type=str, default="文字转语音插件", description="插件描述", required=True),
|
||||
},
|
||||
"components": {"enable_tts": ConfigField(type=bool, default=True, description="是否启用TTS Action")},
|
||||
"logging": {
|
||||
"level": ConfigField(
|
||||
type=str, default="INFO", description="日志记录级别", choices=["DEBUG", "INFO", "WARNING", "ERROR"]
|
||||
),
|
||||
"prefix": ConfigField(type=str, default="[TTS]", description="日志记录前缀"),
|
||||
},
|
||||
}
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
"""返回插件包含的组件列表"""
|
||||
|
||||
# 从配置获取组件启用状态
|
||||
enable_tts = self.get_config("components.enable_tts", True)
|
||||
components = [] # 添加Action组件
|
||||
if enable_tts:
|
||||
components.append((TTSAction.get_action_info(), TTSAction))
|
||||
|
||||
return components
|
||||
Reference in New Issue
Block a user