From 3800e96d143af79ef8e4e67dc81d5db84ba7cb8e Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Thu, 6 Feb 2025 15:10:24 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dmetadata=E4=B8=8D?= =?UTF-8?q?=E7=94=9F=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98=20feat:=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=9F=A5=E7=9C=8B=E6=8F=92=E4=BB=B6=E8=A1=8C?= =?UTF-8?q?=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/provider/sources/gemini_source.py | 2 +- .../core/provider/sources/openai_source.py | 4 +- astrbot/core/star/filter/regex.py | 1 + astrbot/core/star/register/star_handler.py | 4 +- astrbot/core/star/star.py | 3 + astrbot/core/star/star_handler.py | 6 +- astrbot/core/star/star_manager.py | 29 +++++--- astrbot/dashboard/routes/plugin.py | 65 ++++++++++++++++- dashboard/package.json | 2 +- .../full/vertical-sidebar/VerticalSidebar.vue | 2 +- dashboard/src/views/ExtensionPage.vue | 72 ++++++++++++++++--- 11 files changed, 163 insertions(+), 27 deletions(-) diff --git a/astrbot/core/provider/sources/gemini_source.py b/astrbot/core/provider/sources/gemini_source.py index 63cf7f9a2..b91facf74 100644 --- a/astrbot/core/provider/sources/gemini_source.py +++ b/astrbot/core/provider/sources/gemini_source.py @@ -196,7 +196,7 @@ class ProviderGoogleGenAI(Provider): if retry_cnt == 0: llm_response = LLMResponse("err", "err: 请尝试 /reset 重置会话") elif "Function calling is not enabled" in str(e): - logger.info(f"{self.get_model()} 不支持函数调用工具调用,已经自动去除") + logger.info(f"{self.get_model()} 不支持函数工具调用,已自动去除,不影响使用。") if 'tools' in payloads: del payloads['tools'] llm_response = await self._query(payloads, None) diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index 4b4b678ec..c4c7c809b 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -165,7 +165,7 @@ class ProviderOpenAIOfficial(Provider): or 'Function call is not supported' in str(e) \ or 'Function calling is not enabled' in str(e) \ or 'Tool calling is not supported' in str(e): # siliconcloud - logger.info(f"{self.get_model()} 不支持函数调用工具调用,已经自动去除") + logger.info(f"{self.get_model()} 不支持函数工具调用,已自动去除,不影响使用。") if 'tools' in payloads: del payloads['tools'] llm_response = await self._query(payloads, None) @@ -173,7 +173,7 @@ class ProviderOpenAIOfficial(Provider): logger.error(f"发生了错误。Provider 配置如下: {self.provider_config}") if 'tool' in str(e).lower() and 'support' in str(e).lower(): - logger.error(f"疑似该模型不支持函数调用工具调用。请输入 /tool off_all") + logger.error("疑似该模型不支持函数调用工具调用。请输入 /tool off_all") if 'Connection error.' in str(e): proxy = os.environ.get("http_proxy", None) diff --git a/astrbot/core/star/filter/regex.py b/astrbot/core/star/filter/regex.py index c5f919ee3..816b14109 100644 --- a/astrbot/core/star/filter/regex.py +++ b/astrbot/core/star/filter/regex.py @@ -8,6 +8,7 @@ from astrbot.core.config import AstrBotConfig class RegexFilter(HandlerFilter): '''正则表达式过滤器''' def __init__(self, regex: str): + self.regex_str = regex self.regex = re.compile(regex) def filter(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool: diff --git a/astrbot/core/star/register/star_handler.py b/astrbot/core/star/register/star_handler.py index 5d7489d35..2a5e40a63 100644 --- a/astrbot/core/star/register/star_handler.py +++ b/astrbot/core/star/register/star_handler.py @@ -39,13 +39,13 @@ def get_handler_or_create( ) # 插件handler的附加额外信息 + if handler.__doc__: + md.desc = handler.__doc__.strip() if 'desc' in kwargs: md.desc = kwargs['desc'] del kwargs['desc'] md.extras_configs = kwargs - if handler.__doc__: - md.desc = handler.__doc__.strip() if not dont_add: star_handlers_registry.append(md) return md diff --git a/astrbot/core/star/star.py b/astrbot/core/star/star.py index c2abb6e34..862d8d036 100644 --- a/astrbot/core/star/star.py +++ b/astrbot/core/star/star.py @@ -39,6 +39,9 @@ class StarMetadata: config: AstrBotConfig = None '''插件配置''' + + star_handler_full_names: List[str] = None + '''注册的 Handler 的全名列表''' def __str__(self) -> str: return f"StarMetadata({self.name}, {self.desc}, {self.version}, {self.repo})" \ No newline at end of file diff --git a/astrbot/core/star/star_handler.py b/astrbot/core/star/star_handler.py index 389935a37..2d96ac8c3 100644 --- a/astrbot/core/star/star_handler.py +++ b/astrbot/core/star/star_handler.py @@ -52,7 +52,11 @@ class StarHandlerRegistry(Generic[T]): def remove(self, handler: StarHandlerMetadata): '''删除一个 Handler''' - self._handlers.remove(handler) + # self._handlers.remove(handler) + for i, h in enumerate(self._handlers): + if h[1] == handler: + self._handlers.pop(i) + break del self.star_handlers_map[handler.handler_full_name] def __iter__(self): diff --git a/astrbot/core/star/star_manager.py b/astrbot/core/star/star_manager.py index e06908605..72e74f366 100644 --- a/astrbot/core/star/star_manager.py +++ b/astrbot/core/star/star_manager.py @@ -9,7 +9,6 @@ import logging from types import ModuleType from typing import List from astrbot.core.config.astrbot_config import AstrBotConfig -from astrbot.core.config.default import DEFAULT_VALUE_MAP from astrbot.core import logger, sp, pip_installer from .context import Context from . import StarMetadata @@ -127,7 +126,7 @@ class PluginManager: if isinstance(metadata, dict): if 'name' not in metadata or 'desc' not in metadata or 'version' not in metadata or 'author' not in metadata: - raise Exception("插件元数据信息不完整。") + raise Exception("插件元数据信息不完整。name, desc, version, author 是必须的字段。") metadata = StarMetadata( name=metadata['name'], author=metadata['author'], @@ -203,6 +202,18 @@ class PluginManager: # 通过装饰器的方式注册插件 metadata = star_map[path] + try: + # yaml 文件的元数据优先 + metadata_yaml = self._load_plugin_metadata(plugin_path=plugin_dir_path) + if metadata_yaml: + metadata.name = metadata_yaml.name + metadata.author = metadata_yaml.author + metadata.desc = metadata_yaml.desc + metadata.version = metadata_yaml.version + metadata.repo = metadata_yaml.repo + except Exception: + pass + if plugin_config: metadata.config = plugin_config try: @@ -219,7 +230,6 @@ class PluginManager: # 绑定 handler related_handlers = star_handlers_registry.get_handlers_by_module_name(metadata.module_path) for handler in related_handlers: - logger.debug(f"bind handler {handler.handler_name} to {metadata.name}") handler.handler = functools.partial(handler.handler, metadata.star_cls) # 绑定 llm_tool handler for func_tool in llm_tools.func_list: @@ -243,8 +253,7 @@ class PluginManager: obj = getattr(module, classes[0])(context=self.context) # 实例化插件类 metadata = None - plugin_path = os.path.join(self.plugin_store_path, root_dir_name) if not reserved else os.path.join(self.reserved_plugin_path, root_dir_name) - metadata = self._load_plugin_metadata(plugin_path=plugin_path, plugin_obj=obj) + metadata = self._load_plugin_metadata(plugin_path=plugin_dir_path, plugin_obj=obj) metadata.star_cls = obj metadata.config = plugin_config metadata.module = module @@ -260,10 +269,12 @@ class PluginManager: if metadata.module_path in inactivated_plugins: metadata.activated = False - # 检查并且植入自定义的权限过滤器(alter_cmd) + full_names = [] for handler in star_handlers_registry.get_handlers_by_module_name(metadata.module_path): + full_names.append(handler.handler_full_name) + + # 检查并且植入自定义的权限过滤器(alter_cmd) if metadata.name in alter_cmd and handler.handler_name in alter_cmd[metadata.name]: - # 注入权限过滤器 cmd_type = alter_cmd[metadata.name][handler.handler_name].get("permission", "member") found_permission_filter = False for filter_ in handler.event_filters: @@ -279,6 +290,8 @@ class PluginManager: logger.debug(f"插入权限过滤器 {cmd_type} 到 {metadata.name} 的 {handler.handler_name} 方法。") + metadata.star_handler_full_names = full_names + # 执行 initialize() 方法 if hasattr(metadata.star_cls, "initialize"): await metadata.star_cls.initialize() @@ -290,7 +303,7 @@ class PluginManager: # 清除 pip.main 导致的多余的 logging handlers for handler in logging.root.handlers[:]: logging.root.removeHandler(handler) - + if not fail_rec: return True, None else: diff --git a/astrbot/dashboard/routes/plugin.py b/astrbot/dashboard/routes/plugin.py index 56600224f..63cb6ce16 100644 --- a/astrbot/dashboard/routes/plugin.py +++ b/astrbot/dashboard/routes/plugin.py @@ -6,6 +6,12 @@ from astrbot.core import logger from quart import request from astrbot.core.star.star_manager import PluginManager from astrbot.core.core_lifecycle import AstrBotCoreLifecycle +from astrbot.core.star.star_handler import star_handlers_registry +from astrbot.core.star.filter.command import CommandFilter +from astrbot.core.star.filter.command_group import CommandGroupFilter +from astrbot.core.star.filter.permission import PermissionTypeFilter +from astrbot.core.star.filter.regex import RegexFilter +from astrbot.core.star.star_handler import EventType class PluginRoute(Route): def __init__(self, context: RouteContext, core_lifecycle: AstrBotCoreLifecycle, plugin_manager: PluginManager) -> None: @@ -23,6 +29,15 @@ class PluginRoute(Route): self.core_lifecycle = core_lifecycle self.plugin_manager = plugin_manager self.register_routes() + + self.translated_event_type = { + EventType.AdapterMessageEvent: "平台消息下发时", + EventType.OnLLMRequestEvent: "LLM 请求时", + EventType.OnLLMResponseEvent: "LLM 响应后", + EventType.OnDecoratingResultEvent: "回复消息前", + EventType.OnCallingFuncToolEvent: "函数工具", + EventType.OnAfterMessageSentEvent: "发送消息后" + } async def get_online_plugins(self): custom = request.args.get("custom_registry") @@ -59,10 +74,58 @@ class PluginRoute(Route): "desc": plugin.desc, "version": plugin.version, "reserved": plugin.reserved, - "activated": plugin.activated + "activated": plugin.activated, + "handlers": await self.get_plugin_handlers_info(plugin.star_handler_full_names) } _plugin_resp.append(_t) return Response().ok(_plugin_resp).__dict__ + + async def get_plugin_handlers_info(self, handler_full_names: list[str]): + '''解析插件行为''' + handlers = [] + + for handler_full_name in handler_full_names: + info = {} + handler = star_handlers_registry.star_handlers_map.get(handler_full_name, None) + if handler is None: + continue + info["event_type"] = handler.event_type.name + info["event_type_h"] = self.translated_event_type.get(handler.event_type, handler.event_type.name) + info["handler_full_name"] = handler.handler_full_name + info["desc"] = handler.desc + info["handler_name"] = handler.handler_name + + if handler.event_type == EventType.AdapterMessageEvent: + # 处理平台适配器消息事件 + has_admin = False + for filter in handler.event_filters: # 正常handler就只有 1~2 个 filter,因此这里时间复杂度不会太高 + if isinstance(filter, CommandFilter): + info["type"] = "指令" + info["cmd"] = filter.command_name + elif isinstance(filter, CommandGroupFilter): + info["type"] = "指令组" + info["cmd"] = filter.group_name + info["sub_command"] = filter.print_cmd_tree(filter.sub_command_filters) + elif isinstance(filter, RegexFilter): + info["type"] = "正则匹配" + info["cmd"] = filter.regex_str + elif isinstance(filter, PermissionTypeFilter): + has_admin = True + info["has_admin"] = has_admin + if "cmd" not in info: + info["cmd"] = "未知" + if "type" not in info: + info["type"] = "事件监听器" + else: + info["cmd"] = "自动触发" + info["type"] = "无" + + if not info["desc"]: + info["desc"] = "无描述" + + handlers.append(info) + + return handlers async def install_plugin(self): post_data = await request.json diff --git a/dashboard/package.json b/dashboard/package.json index 9b59cfa92..5d76b72d5 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -32,7 +32,7 @@ "vue-router": "4.2.4", "vue3-apexcharts": "1.4.4", "vue3-print-nb": "0.1.4", - "vuetify": "3.3.14", + "vuetify": "3.7.11", "yup": "1.2.0" }, "devDependencies": { diff --git a/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue b/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue index 58afcbea2..ae9697571 100644 --- a/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue +++ b/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue @@ -9,7 +9,7 @@ const sidebarMenu = shallowRef(sidebarItems);