Skip to content

Commit

Permalink
Merge pull request #735 from RockChinQ/feat/plugin-api
Browse files Browse the repository at this point in the history
Feat: 插件异步 API
  • Loading branch information
RockChinQ authored Mar 22, 2024
2 parents f53070d + 80258e9 commit 995d1f6
Show file tree
Hide file tree
Showing 34 changed files with 476 additions and 58 deletions.
25 changes: 25 additions & 0 deletions pkg/command/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class CommandReturn(pydantic.BaseModel):
image: typing.Optional[mirai.Image]

error: typing.Optional[errors.CommandError]= None
"""错误
"""

class Config:
arbitrary_types_allowed = True
Expand All @@ -30,17 +32,40 @@ class ExecuteContext(pydantic.BaseModel):
"""

query: core_entities.Query
"""本次消息的请求对象"""

session: core_entities.Session
"""本次消息所属的会话对象"""

command_text: str
"""命令完整文本"""

command: str
"""命令名称"""

crt_command: str
"""当前命令
多级命令中crt_command为当前命令,command为根命令。
例如:!plugin on Webwlkr
处理到plugin时,command为plugin,crt_command为plugin
处理到on时,command为plugin,crt_command为on
"""

params: list[str]
"""命令参数
整个命令以空格分割后的参数列表
"""

crt_params: list[str]
"""当前命令参数
多级命令中crt_params为当前命令参数,params为根命令参数。
例如:!plugin on Webwlkr
处理到plugin时,params为['on', 'Webwlkr'],crt_params为['on', 'Webwlkr']
处理到on时,params为['on', 'Webwlkr'],crt_params为['Webwlkr']
"""

privilege: int
"""发起人权限"""
20 changes: 19 additions & 1 deletion pkg/command/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ def decorator(cls: typing.Type[CommandOperator]) -> typing.Type[CommandOperator]

class CommandOperator(metaclass=abc.ABCMeta):
"""命令算子抽象类
以下的参数均不需要在子类中设置,只需要在使用装饰器注册类时作为参数传递即可。
命令支持级联,即一个命令可以有多个子命令,子命令可以有子命令,以此类推。
处理命令时,若有子命令,会以当前参数列表的第一个参数去匹配子命令,若匹配成功,则转移到子命令中执行。
若没有匹配成功或没有子命令,则执行当前命令。
"""

ap: app.Application
Expand All @@ -60,7 +65,8 @@ class CommandOperator(metaclass=abc.ABCMeta):
"""名称,搜索到时若符合则使用"""

path: str
"""路径,所有父节点的name的连接,用于定义命令权限"""
"""路径,所有父节点的name的连接,用于定义命令权限,由管理器在初始化时自动设置。
"""

alias: list[str]
"""同name"""
Expand All @@ -69,6 +75,7 @@ class CommandOperator(metaclass=abc.ABCMeta):
"""此节点的帮助信息"""

usage: str = None
"""用法"""

parent_class: typing.Union[typing.Type[CommandOperator], None] = None
"""父节点类。标记以供管理器在初始化时编织父子关系。"""
Expand All @@ -92,4 +99,15 @@ async def execute(
self,
context: entities.ExecuteContext
) -> typing.AsyncGenerator[entities.CommandReturn, None]:
"""实现此方法以执行命令
支持多次yield以返回多个结果。
例如:一个安装插件的命令,可能会有下载、解压、安装等多个步骤,每个步骤都可以返回一个结果。
Args:
context (entities.ExecuteContext): 命令执行上下文
Yields:
entities.CommandReturn: 命令返回封装
"""
pass
32 changes: 15 additions & 17 deletions pkg/config/impls/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@
class JSONConfigFile(file_model.ConfigFile):
"""JSON配置文件"""

config_file_name: str = None
"""配置文件名"""

template_file_name: str = None
"""模板文件名"""

def __init__(self, config_file_name: str, template_file_name: str) -> None:
def __init__(
self, config_file_name: str, template_file_name: str = None, template_data: dict = None
) -> None:
self.config_file_name = config_file_name
self.template_file_name = template_file_name
self.template_data = template_data

def exists(self) -> bool:
return os.path.exists(self.config_file_name)
Expand All @@ -29,23 +26,24 @@ async def load(self) -> dict:
if not self.exists():
await self.create()

with open(self.config_file_name, 'r', encoding='utf-8') as f:
cfg = json.load(f)
if self.template_file_name is not None:
with open(self.config_file_name, "r", encoding="utf-8") as f:
cfg = json.load(f)

# 从模板文件中进行补全
with open(self.template_file_name, 'r', encoding='utf-8') as f:
template_cfg = json.load(f)
with open(self.template_file_name, "r", encoding="utf-8") as f:
self.template_data = json.load(f)

for key in template_cfg:
for key in self.template_data:
if key not in cfg:
cfg[key] = template_cfg[key]
cfg[key] = self.template_data[key]

return cfg

async def save(self, cfg: dict):
with open(self.config_file_name, 'w', encoding='utf-8') as f:
with open(self.config_file_name, "w", encoding="utf-8") as f:
json.dump(cfg, f, indent=4, ensure_ascii=False)

def save_sync(self, cfg: dict):
with open(self.config_file_name, 'w', encoding='utf-8') as f:
json.dump(cfg, f, indent=4, ensure_ascii=False)
with open(self.config_file_name, "w", encoding="utf-8") as f:
json.dump(cfg, f, indent=4, ensure_ascii=False)
5 changes: 3 additions & 2 deletions pkg/config/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ async def load_python_module_config(config_name: str, template_name: str) -> Con
return cfg_mgr


async def load_json_config(config_name: str, template_name: str) -> ConfigManager:
async def load_json_config(config_name: str, template_name: str=None, template_data: dict=None) -> ConfigManager:
"""加载JSON配置文件"""
cfg_inst = json_file.JSONConfigFile(
config_name,
template_name
template_name,
template_data
)

cfg_mgr = ConfigManager(cfg_inst)
Expand Down
1 change: 1 addition & 0 deletions pkg/config/migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@


preregistered_migrations: list[typing.Type[Migration]] = []
"""当前阶段暂不支持扩展"""

def migration_class(name: str, number: int):
"""注册一个迁移
Expand Down
3 changes: 3 additions & 0 deletions pkg/config/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ class ConfigFile(metaclass=abc.ABCMeta):
template_file_name: str = None
"""模板文件名"""

template_data: dict = None
"""模板数据"""

@abc.abstractmethod
def exists(self) -> bool:
pass
Expand Down
7 changes: 3 additions & 4 deletions pkg/core/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
class Application:
"""运行时应用对象和上下文"""

im_mgr: im_mgr.PlatformManager = None
platform_mgr: im_mgr.PlatformManager = None

cmd_mgr: cmdmgr.CommandManager = None

Expand Down Expand Up @@ -85,10 +85,9 @@ async def run(self):
tasks = []

try:



tasks = [
asyncio.create_task(self.im_mgr.run()),
asyncio.create_task(self.platform_mgr.run()),
asyncio.create_task(self.ctrl.run())
]

Expand Down
1 change: 0 additions & 1 deletion pkg/core/bootutils/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"botpy": "qq-botpy",
"PIL": "pillow",
"nakuru": "nakuru-project-idk",
"CallingGPT": "CallingGPT",
"tiktoken": "tiktoken",
"yaml": "pyyaml",
"aiohttp": "aiohttp",
Expand Down
26 changes: 13 additions & 13 deletions pkg/core/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,43 +32,43 @@ class Query(pydantic.BaseModel):
"""请求ID,添加进请求池时生成"""

launcher_type: LauncherTypes
"""会话类型,platform设置"""
"""会话类型,platform处理阶段设置"""

launcher_id: int
"""会话ID,platform设置"""
"""会话ID,platform处理阶段设置"""

sender_id: int
"""发送者ID,platform设置"""
"""发送者ID,platform处理阶段设置"""

message_event: mirai.MessageEvent
"""事件,platform收到的事件"""
"""事件,platform收到的原始事件"""

message_chain: mirai.MessageChain
"""消息链,platform收到的消息链"""
"""消息链,platform收到的原始消息链"""

adapter: msadapter.MessageSourceAdapter
"""适配器对象"""
"""消息平台适配器对象,单个app中可能启用了多个消息平台适配器,此对象表明发起此query的适配器"""

session: typing.Optional[Session] = None
"""会话对象,由前置处理器设置"""
"""会话对象,由前置处理器阶段设置"""

messages: typing.Optional[list[llm_entities.Message]] = []
"""历史消息列表,由前置处理器设置"""
"""历史消息列表,由前置处理器阶段设置"""

prompt: typing.Optional[sysprompt_entities.Prompt] = None
"""情景预设内容,由前置处理器设置"""
"""情景预设内容,由前置处理器阶段设置"""

user_message: typing.Optional[llm_entities.Message] = None
"""此次请求的用户消息对象,由前置处理器设置"""
"""此次请求的用户消息对象,由前置处理器阶段设置"""

use_model: typing.Optional[entities.LLMModelInfo] = None
"""使用的模型,由前置处理器设置"""
"""使用的模型,由前置处理器阶段设置"""

use_funcs: typing.Optional[list[tools_entities.LLMFunction]] = None
"""使用的函数,由前置处理器设置"""
"""使用的函数,由前置处理器阶段设置"""

resp_messages: typing.Optional[list[llm_entities.Message]] = []
"""由provider生成的回复消息对象列表"""
"""由Process阶段生成的回复消息对象列表"""

resp_message_chain: typing.Optional[mirai.MessageChain] = None
"""回复消息链,从resp_messages包装而得"""
Expand Down
5 changes: 4 additions & 1 deletion pkg/core/stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@


preregistered_stages: dict[str, typing.Type[BootingStage]] = {}
"""预注册的请求处理阶段。在初始化时,所有请求处理阶段类会被注册到此字典中。"""
"""预注册的请求处理阶段。在初始化时,所有请求处理阶段类会被注册到此字典中。
当前阶段暂不支持扩展
"""

def stage_class(
name: str
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/stages/build_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ async def run(self, ap: app.Application):

im_mgr_inst = im_mgr.PlatformManager(ap=ap)
await im_mgr_inst.initialize()
ap.im_mgr = im_mgr_inst
ap.platform_mgr = im_mgr_inst

stage_mgr = stagemgr.StageManager(ap)
await stage_mgr.initialize()
Expand Down
15 changes: 12 additions & 3 deletions pkg/pipeline/cntfilter/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,24 @@ class EnableStage(enum.Enum):

class FilterResult(pydantic.BaseModel):
level: ResultLevel
"""结果等级
对于前置处理阶段,只要有任意一个返回 非PASS 的内容过滤器结果,就会中断处理。
对于后置处理阶段,当且内容过滤器返回 BLOCK 时,会中断处理。
"""

replacement: str
"""替换后的消息"""
"""替换后的消息
内容过滤器可以进行一些遮掩处理,然后把遮掩后的消息返回。
若没有修改内容,也需要返回原消息。
"""

user_notice: str
"""不通过时,用户提示消息"""
"""不通过时,若此值不为空,将对用户提示消息"""

console_notice: str
"""不通过时,控制台提示消息"""
"""不通过时,若此值不为空,将在控制台提示消息"""


class ManagerResultLevel(enum.Enum):
Expand Down
14 changes: 14 additions & 0 deletions pkg/pipeline/cntfilter/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ def __init__(self, ap: app.Application):
@property
def enable_stages(self):
"""启用的阶段
默认为消息请求AI前后的两个阶段。
entity.EnableStage.PRE: 消息请求AI前,此时需要检查的内容是用户的输入消息。
entity.EnableStage.POST: 消息请求AI后,此时需要检查的内容是AI的回复消息。
"""
return [
entities.EnableStage.PRE,
Expand All @@ -60,5 +65,14 @@ async def initialize(self):
@abc.abstractmethod
async def process(self, message: str) -> entities.FilterResult:
"""处理消息
分为前后阶段,具体取决于 enable_stages 的值。
对于内容过滤器来说,不需要考虑消息所处的阶段,只需要检查消息内容即可。
Args:
message (str): 需要检查的内容
Returns:
entities.FilterResult: 过滤结果,具体内容请查看 entities.FilterResult 类的文档
"""
raise NotImplementedError
2 changes: 1 addition & 1 deletion pkg/pipeline/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ async def _check_output(self, query: entities.Query, result: pipeline_entities.S
"""检查输出
"""
if result.user_notice:
await self.ap.im_mgr.send(
await self.ap.platform_mgr.send(
query.message_event,
result.user_notice,
query.adapter
Expand Down
20 changes: 20 additions & 0 deletions pkg/pipeline/longtext/strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@
def strategy_class(
name: str
) -> typing.Callable[[typing.Type[LongTextStrategy]], typing.Type[LongTextStrategy]]:
"""长文本处理策略类装饰器
Args:
name (str): 策略名称
Returns:
typing.Callable[[typing.Type[LongTextStrategy]], typing.Type[LongTextStrategy]]: 装饰器
"""

def decorator(cls: typing.Type[LongTextStrategy]) -> typing.Type[LongTextStrategy]:
assert issubclass(cls, LongTextStrategy)

Expand Down Expand Up @@ -43,4 +52,15 @@ async def initialize(self):

@abc.abstractmethod
async def process(self, message: str, query: core_entities.Query) -> list[MessageComponent]:
"""处理长文本
在 platform.json 中配置 long-text-process 字段,只要 文本长度超过了 threshold 就会调用此方法
Args:
message (str): 消息
query (core_entities.Query): 此次请求的上下文对象
Returns:
list[mirai.models.messages.MessageComponent]: 转换后的 YiriMirai 消息组件列表
"""
return []
Loading

0 comments on commit 995d1f6

Please sign in to comment.