perf: 结构化插件的表示格式; 优化插件开发接口

This commit is contained in:
Soulter
2024-03-12 18:50:50 +08:00
parent 1af481f5f9
commit e643eea365
22 changed files with 457 additions and 215 deletions

View File

@@ -29,12 +29,13 @@ from util import general_utils as gu
from util.general_utils import Logger, upload, run_monitor
from util.cmd_config import CmdConfig as cc
from util.cmd_config import init_astrbot_config_items
from . global_object import GlobalObject
from .types import *
from addons.dashboard.helper import DashBoardHelper
from addons.dashboard.server import DashBoardData
from cores.database.conn import dbConn
from model.platform._message_result import MessageResult
# 用户发言频率
user_frequency = {}
# 时间默认值
@@ -43,7 +44,7 @@ frequency_time = 60
frequency_count = 10
# 版本
version = '3.1.6'
version = '3.1.7'
# 语言模型
REV_CHATGPT = 'rev_chatgpt'
@@ -98,9 +99,6 @@ def init(cfg):
_global_object = GlobalObject()
_global_object.version = version
_global_object.base_config = cfg
_global_object.stat['session'] = {}
_global_object.stat['message'] = {}
_global_object.stat['platform'] = {}
_global_object.logger = logger
logger.log("AstrBot v"+version, gu.LEVEL_INFO)
@@ -125,6 +123,7 @@ def init(cfg):
llm_instance[REV_CHATGPT] = ProviderRevChatGPT(cfg['rev_ChatGPT'], base_url=cc.get("CHATGPT_BASE_URL", None))
llm_command_instance[REV_CHATGPT] = CommandRevChatGPT(llm_instance[REV_CHATGPT], _global_object)
chosen_provider = REV_CHATGPT
_global_object.llms.append(RegisteredLLM(llm_name=REV_CHATGPT, llm_instance=llm_instance[REV_CHATGPT], origin="internal"))
else:
input("请退出本程序, 然后在配置文件中填写rev_ChatGPT相关配置")
if OPENAI_OFFICIAL in prov:
@@ -134,6 +133,7 @@ def init(cfg):
from model.command.openai_official import CommandOpenAIOfficial
llm_instance[OPENAI_OFFICIAL] = ProviderOpenAIOfficial(cfg['openai'])
llm_command_instance[OPENAI_OFFICIAL] = CommandOpenAIOfficial(llm_instance[OPENAI_OFFICIAL], _global_object)
_global_object.llms.append(RegisteredLLM(llm_name=OPENAI_OFFICIAL, llm_instance=llm_instance[OPENAI_OFFICIAL], origin="internal"))
chosen_provider = OPENAI_OFFICIAL
# 检查provider设置偏好
@@ -184,7 +184,7 @@ def init(cfg):
_command = Command(None, _global_object)
ok, err = putil.plugin_reload(_global_object.cached_plugins)
if ok:
logger.log(f"成功载入{len(_global_object.cached_plugins)}个插件", gu.LEVEL_INFO)
logger.log(f"成功载入 {len(_global_object.cached_plugins)} 个插件", gu.LEVEL_INFO)
else:
logger.log(err, gu.LEVEL_ERROR)
@@ -244,7 +244,7 @@ def run_qqchan_bot(cfg: dict, global_object: GlobalObject):
try:
from model.platform.qq_official import QQOfficial
qqchannel_bot = QQOfficial(cfg=cfg, message_handler=oper_msg, global_object=global_object)
global_object.platform_qqchan = qqchannel_bot
global_object.platforms.append(RegisteredPlatform(platform_name="qq_official", platform_instance=qqchannel_bot, origin="internal"))
qqchannel_bot.run()
except BaseException as e:
logger.log("启动QQ频道机器人时出现错误, 原因如下: " + str(e), gu.LEVEL_CRITICAL, tag="QQ频道")
@@ -272,7 +272,7 @@ def run_gocq_bot(cfg: dict, _global_object: GlobalObject):
break
try:
qq_gocq = QQGOCQ(cfg=cfg, message_handler=oper_msg, global_object=_global_object)
_global_object.platform_qq = qq_gocq
_global_object.platforms.append(RegisteredPlatform(platform_name="gocq", platform_instance=qq_gocq, origin="internal"))
qq_gocq.run()
except BaseException as e:
input("启动QQ机器人出现错误"+str(e))
@@ -317,7 +317,7 @@ async def oper_msg(message: Union[GroupMessage, FriendMessage, GuildMessage, Nak
message: 消息对象
session_id: 该消息源的唯一识别号
role: member | admin
platform: 平台(gocq, qqchan)
platform: str 所注册的平台的名称。如果没有注册,将抛出一个异常。
"""
global chosen_provider, _global_object
message_str = ''
@@ -326,6 +326,16 @@ async def oper_msg(message: Union[GroupMessage, FriendMessage, GuildMessage, Nak
hit = False # 是否命中指令
command_result = () # 调用指令返回的结果
# 获取平台实例
reg_platform: RegisteredPlatform = None
for p in _global_object.platforms:
if p.platform_name == platform:
reg_platform = p
break
if not reg_platform:
_global_object.logger.log(f"未找到平台 {platform} 的实例。", gu.LEVEL_ERROR)
raise Exception(f"未找到平台 {platform} 的实例。")
# 统计数据,如频道消息量
await record_message(platform, session_id)
@@ -365,7 +375,7 @@ async def oper_msg(message: Union[GroupMessage, FriendMessage, GuildMessage, Nak
message_str,
session_id,
role,
platform,
reg_platform,
message,
)

View File

@@ -1,90 +0,0 @@
from model.platform.qq_official import QQOfficial, NakuruGuildMessage
from model.platform.qq_gocq import QQGOCQ
from model.provider.provider import Provider
from addons.dashboard.server import DashBoardData
from nakuru import (
GroupMessage,
FriendMessage,
GuildMessage,
)
from typing import Union
class GlobalObject:
'''
存放一些公用的数据,用于在不同模块(如core与command)之间传递
'''
version: str
nick: str # gocq 的昵称
base_config: dict # config.json
cached_plugins: dict # 缓存的插件
web_search: bool # 是否开启了网页搜索
reply_prefix: str
admin_qq: str
admin_qqchan: str
unique_session: bool
cnt_total: int
platform_qq: QQGOCQ
platform_qqchan: QQOfficial
default_personality: dict
dashboard_data: DashBoardData
stat: dict
logger: None
def __init__(self):
self.nick = None # gocq 的昵称
self.base_config = None # config.yaml
self.cached_plugins = {} # 缓存的插件
self.web_search = False # 是否开启了网页搜索
self.reply_prefix = None
self.admin_qq = "123456"
self.admin_qqchan = "123456"
self.unique_session = False
self.cnt_total = 0
self.platform_qq = None
self.platform_qqchan = None
self.default_personality = None
self.dashboard_data = None
self.stat = {}
class AstrMessageEvent():
message_str: str # 纯消息字符串
message_obj: Union[GroupMessage, FriendMessage, GuildMessage, NakuruGuildMessage] # 消息对象
gocq_platform: QQGOCQ
qq_sdk_platform: QQOfficial
platform: str # `gocq` 或 `qqchan`
role: str # `admin` 或 `member`
global_object: GlobalObject # 一些公用数据
session_id: int # 会话id (可能是群id也可能是某个user的id。取决于是否开启了 unique_session)
def __init__(self, message_str: str,
message_obj: Union[GroupMessage, FriendMessage, GuildMessage, NakuruGuildMessage],
gocq_platform: QQGOCQ,
qq_sdk_platform: QQOfficial,
platform: str,
role: str,
global_object: GlobalObject,
llm_provider: Provider = None,
session_id: int = None):
self.message_str = message_str
self.message_obj = message_obj
self.gocq_platform = gocq_platform
self.qq_sdk_platform = qq_sdk_platform
self.platform = platform
self.role = role
self.global_object = global_object
self.llm_provider = llm_provider
self.session_id = session_id
class CommandResult():
'''
用于在Command中返回多个值
'''
def __init__(self, hit: bool, success: bool, message_chain: list, command_name: str = "unknown_command") -> None:
self.hit = hit
self.success = success
self.message_chain = message_chain
self.command_name = command_name
def _result_tuple(self):
return (self.success, self.message_chain, self.command_name)

140
cores/qqbot/types.py Normal file
View File

@@ -0,0 +1,140 @@
from model.platform.qq_official import NakuruGuildMessage
from model.provider.provider import Provider as LLMProvider
from model.platform._platfrom import Platform
from nakuru import (
GroupMessage,
FriendMessage,
GuildMessage,
)
from typing import Union, List, ClassVar
from types import ModuleType
from enum import Enum
from dataclasses import dataclass
class PluginType(Enum):
PLATFORM = 'platfrom' # 平台类插件。
LLM = 'llm' # 大语言模型类插件
COMMON = 'common' # 其他插件
@dataclass
class PluginMetadata:
'''
插件的元数据。
'''
# required
plugin_name: str
plugin_type: PluginType
author: str # 插件作者
desc: str # 插件简介
version: str # 插件版本
# optional
repo: str = None # 插件仓库地址
def __str__(self) -> str:
return f"PluginMetadata({self.plugin_name}, {self.plugin_type}, {self.desc}, {self.version}, {self.repo})"
@dataclass
class RegisteredPlugin:
'''
注册在 AstrBot 中的插件。
'''
metadata: PluginMetadata
plugin_instance: object
module_path: str
module: ModuleType
root_dir_name: str
def __str__(self) -> str:
return f"RegisteredPlugin({self.metadata}, {self.module_path}, {self.root_dir_name})"
RegisteredPlugins = List[RegisteredPlugin]
@dataclass
class RegisteredPlatform:
'''
注册在 AstrBot 中的平台。平台应当实现 Platform 接口。
'''
platform_name: str
platform_instance: Platform
origin: str = None # 注册来源
@dataclass
class RegisteredLLM:
'''
注册在 AstrBot 中的大语言模型调用。大语言模型应当实现 LLMProvider 接口。
'''
llm_name: str
llm_instance: LLMProvider
origin: str = None # 注册来源
class GlobalObject:
'''
存放一些公用的数据,用于在不同模块(如core与command)之间传递
'''
version: str # 机器人版本
nick: str # 用户定义的机器人的别名
base_config: dict # config.json 中导出的配置
cached_plugins: List[RegisteredPlugin] # 加载的插件
platforms: List[RegisteredPlatform]
llms: List[RegisteredLLM]
web_search: bool # 是否开启了网页搜索
reply_prefix: str # 回复前缀
unique_session: bool # 是否开启了独立会话
cnt_total: int # 总消息数
default_personality: dict
dashboard_data = None
logger: None
def __init__(self):
self.nick = None # gocq 的昵称
self.base_config = None # config.yaml
self.cached_plugins = [] # 缓存的插件
self.web_search = False # 是否开启了网页搜索
self.reply_prefix = None
self.unique_session = False
self.cnt_total = 0
self.platforms = []
self.llms = []
self.default_personality = None
self.dashboard_data = None
self.stat = {}
class AstrMessageEvent():
'''
消息事件。
'''
context: GlobalObject # 一些公用数据
message_str: str # 纯消息字符串
message_obj: Union[GroupMessage, FriendMessage, GuildMessage, NakuruGuildMessage] # 消息对象
platform: RegisteredPlatform # 来源平台
role: str # 基本身份。`admin` 或 `member`
session_id: int # 会话 id
def __init__(self,
message_str: str,
message_obj: Union[GroupMessage, FriendMessage, GuildMessage, NakuruGuildMessage],
platform: RegisteredPlatform,
role: str,
context: GlobalObject,
session_id: str = None):
self.context = context
self.message_str = message_str
self.message_obj = message_obj
self.platform = platform
self.role = role
self.session_id = session_id
class CommandResult():
'''
用于在Command中返回多个值
'''
def __init__(self, hit: bool, success: bool, message_chain: list, command_name: str = "unknown_command") -> None:
self.hit = hit
self.success = success
self.message_chain = message_chain
self.command_name = command_name
def _result_tuple(self):
return (self.success, self.message_chain, self.command_name)