diff --git a/README.md b/README.md index 0e457c2..23f3719 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
diff --git a/nonebot_plugin_marshoai/__init__.py b/nonebot_plugin_marshoai/__init__.py index 3a1afa3..23139eb 100644 --- a/nonebot_plugin_marshoai/__init__.py +++ b/nonebot_plugin_marshoai/__init__.py @@ -15,6 +15,8 @@ usage = """MarshoAI Alpha by Asankilp praises : 返回夸赞名单的提示词。 usermsg <消息> : 往当前会话添加用户消息(UserMessage)。 assistantmsg <消息> : 往当前会话添加助手消息(AssistantMessage)。 + savecontext <文件名> : 保存当前会话的上下文至插件数据目录下的contexts/<文件名>.json里。 + loadcontext <文件名> : 从插件数据目录下的contexts/<文件名>.json里读取上下文并覆盖到当前会话。 注意事项: - 当 Marsho 回复消息为None或以content_filter开头的错误信息时,表示该消息被内容过滤器过滤,请调整你的聊天内容确保其合规。 - 当回复以RateLimitReached开头的错误信息时,该 AI 模型的次数配额已用尽,请联系Bot管理员。 diff --git a/nonebot_plugin_marshoai/azure.py b/nonebot_plugin_marshoai/azure.py index 97d46f9..b9c32f4 100644 --- a/nonebot_plugin_marshoai/azure.py +++ b/nonebot_plugin_marshoai/azure.py @@ -20,6 +20,8 @@ praises_cmd = on_command("praises",permission=SUPERUSER) add_usermsg_cmd = on_command("usermsg",permission=SUPERUSER) add_assistantmsg_cmd = on_command("assistantmsg",permission=SUPERUSER) contexts_cmd = on_command("contexts",permission=SUPERUSER) +save_context_cmd = on_command("savecontext",permission=SUPERUSER) +load_context_cmd = on_command("loadcontext",permission=SUPERUSER) marsho_cmd = on_alconna( Alconna( "marsho", @@ -32,14 +34,14 @@ context = MarshoContext() @add_usermsg_cmd.handle() async def add_usermsg(target: MsgTarget, arg: Message = CommandArg()): if msg := arg.extract_plain_text(): - context.append(UserMessage(content=msg), target.id, target.private) - await UniMessage("已添加用户消息").send() + context.append(UserMessage(content=msg).as_dict(), target.id, target.private) + await add_usermsg_cmd.finish("已添加用户消息") @add_assistantmsg_cmd.handle() async def add_assistantmsg(target: MsgTarget, arg: Message = CommandArg()): if msg := arg.extract_plain_text(): - context.append(AssistantMessage(content=msg), target.id, target.private) - await UniMessage("已添加助手消息").send() + context.append(AssistantMessage(content=msg).as_dict(), target.id, target.private) + await add_assistantmsg_cmd.finish("已添加助手消息") @praises_cmd.handle() async def praises(): @@ -49,17 +51,18 @@ async def praises(): async def contexts(target: MsgTarget): await UniMessage(str(context.build(target.id, target.private)[1:])).send() -# @setprompt_cmd.handle() #用不了了 -# async def setprompt(arg: Message = CommandArg()): -# global spell, context -# if prompt := arg.extract_plain_text(): -# spell = SystemMessage(content=prompt) -# await setprompt_cmd.finish("已设置提示词") -# else: -# spell = SystemMessage(content="") -# context = [] -# await setprompt_cmd.finish("已清除提示词") +@save_context_cmd.handle() +async def save_context(target: MsgTarget, arg: Message = CommandArg()): + contexts = context.build(target.id, target.private)[1:] + if msg := arg.extract_plain_text(): + await save_context_to_json(msg, contexts) + await save_context_cmd.finish("已保存上下文") +@load_context_cmd.handle() +async def load_context(target: MsgTarget, arg: Message = CommandArg()): + if msg := arg.extract_plain_text(): + context.set_context(await load_context_from_json(msg), target.id, target.private) + await load_context_cmd.finish("已加载并覆盖上下文") @resetmem_cmd.handle() async def resetmem(target: MsgTarget): @@ -72,6 +75,7 @@ async def changemodel(arg : Message = CommandArg()): if model := arg.extract_plain_text(): model_name = model await changemodel_cmd.finish("已切换") + @marsho_cmd.handle() async def marsho( target: MsgTarget, @@ -121,9 +125,9 @@ async def marsho( ) #await UniMessage(str(response)).send() choice = response.choices[0] - if choice["finish_reason"] == CompletionsFinishReason.STOPPED: - context.append(UserMessage(content=usermsg), target.id, target.private) - context.append(choice.message, target.id, target.private) + if choice["finish_reason"] == CompletionsFinishReason.STOPPED: # 当对话成功时,将dict的上下文添加到上下文类中 + context.append(UserMessage(content=usermsg).as_dict(), target.id, target.private) + context.append(choice.message.as_dict(), target.id, target.private) elif choice["finish_reason"] == CompletionsFinishReason.CONTENT_FILTERED: await UniMessage("*已被内容过滤器过滤。*").send() #await UniMessage(str(choice)).send() diff --git a/nonebot_plugin_marshoai/models.py b/nonebot_plugin_marshoai/models.py index 07f02ac..06c39c1 100644 --- a/nonebot_plugin_marshoai/models.py +++ b/nonebot_plugin_marshoai/models.py @@ -13,7 +13,7 @@ class MarshoContext: def _get_target_dict(self, is_private): return self.contents["private"] if is_private else self.contents["non-private"] - def append(self, content, target_id, is_private): + def append(self, content, target_id: str, is_private: bool): """ 往上下文中添加消息 """ @@ -22,14 +22,21 @@ class MarshoContext: target_dict[target_id] = [] target_dict[target_id].append(content) - def reset(self, target_id, is_private): + def set_context(self, contexts, target_id: str, is_private: bool): + """ + 设置上下文 + """ + target_dict = self._get_target_dict(is_private) + target_dict[target_id] = contexts + + def reset(self, target_id: str, is_private: bool): """ 重置上下文 """ target_dict = self._get_target_dict(is_private) target_dict[target_id].clear() - def build(self, target_id, is_private): + def build(self, target_id: str, is_private: bool) -> list: """ 构建返回的上下文,其中包括系统消息 """ diff --git a/nonebot_plugin_marshoai/util.py b/nonebot_plugin_marshoai/util.py index 0751a18..bf9dee2 100644 --- a/nonebot_plugin_marshoai/util.py +++ b/nonebot_plugin_marshoai/util.py @@ -6,6 +6,7 @@ import httpx import nonebot_plugin_localstore as store from datetime import datetime from zhDateTime import DateTime +from pathlib import Path from azure.ai.inference.models import SystemMessage from .config import config async def get_image_b64(url): @@ -49,6 +50,23 @@ def build_praises(): result.append(f"名字:{item['name']},优点:{item['advantages']}") return "\n".join(result) +async def save_context_to_json(name: str, context: str): + context_dir = store.get_plugin_data_dir() / "contexts" + os.makedirs(context_dir, exist_ok=True) + file_path = os.path.join(context_dir, f"{name}.json") + with open(file_path, 'w', encoding='utf-8') as json_file: + json.dump(context, json_file, ensure_ascii=False, indent=4) + +async def load_context_from_json(name: str): + context_dir = store.get_plugin_data_dir() / "contexts" + os.makedirs(context_dir, exist_ok=True) + file_path = os.path.join(context_dir, f"{name}.json") + try: + with open(file_path, 'r', encoding='utf-8') as json_file: + return json.load(json_file) + except FileNotFoundError: + return [] + def get_prompt(): prompts = "" prompts += config.marshoai_additional_prompt @@ -61,7 +79,7 @@ def get_prompt(): time_prompt = f"现在的时间是{current_time},农历{current_lunar_date}。" prompts += time_prompt marsho_prompt = config.marshoai_prompt - spell = SystemMessage(content=marsho_prompt+prompts) + spell = SystemMessage(content=marsho_prompt+prompts).as_dict() return spell def suggest_solution(errinfo: str) -> str: @@ -70,7 +88,8 @@ def suggest_solution(errinfo: str) -> str: "RateLimitReached": "模型达到调用速率限制。请稍等一段时间或联系Bot管理员。", "tokens_limit_reached": "请求token达到上限。请重置上下文。", "content_length_limit": "请求体过大。请重置上下文。", - "unauthorized": "Azure凭据无效。请联系Bot管理员。" + "unauthorized": "Azure凭据无效。请联系Bot管理员。", + "invalid type: parameter messages.content is of type array but should be of type string.": "请重置上下文。" } for key, suggestion in suggestions.items(): diff --git a/pyproject.toml b/pyproject.toml index 2935916..6e58da1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "nonebot-plugin-marshoai" -version = "0.1.4" +version = "0.2" description = "Nonebot2插件,调用Azure OpenAI服务实现猫娘聊天" readme = "README.md" requires-python = "<4.0,>=3.9" @@ -22,6 +22,7 @@ Homepage = "https://github.com/LiteyukiStudio/nonebot-plugin-marshoai" [tool.nonebot] plugins = ["nonebot_plugin_marshoai"] +adapters = [{name = "OneBot V11", module_name = "nonebot.adapters.onebot.v11"}] [tool.pdm]