Compare commits

...

7 Commits

Author SHA1 Message Date
Muika
6050fd1f20 🐛 修复 get_message_id 被弃用的问题并简单修缮了代码格式 (#32)
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: Akarin~ <60691961+Asankilp@users.noreply.github.com>
2025-08-16 17:02:32 +08:00
a18d85d45c ✏️ 更新主页链接 2025-06-21 19:51:14 +08:00
Akarin~
dc6786deab 暗语初步支持 (#27) 2025-06-10 13:30:54 +08:00
6bfa2c39a1 Merge pull request #28 from LiteyukiStudio/snowykami-patch-3
📝 new page
2025-06-10 13:02:32 +08:00
2ce29e45e7 📝 new page 2025-06-10 12:58:52 +08:00
55f9c427b7 🗑️ 标记MarshoTools为弃用 2025-04-05 01:31:35 +08:00
Akarin~
5768b95b09 [WIP] 表情回应支持 (#26)
* 初步支持&utils重构

* 戳一戳支持流式请求

* 移除未使用import

* 解决类型问题
2025-04-04 23:01:01 +08:00
16 changed files with 256 additions and 128 deletions

View File

@@ -52,7 +52,7 @@ jobs:
- name: "发布" - name: "发布"
run: | run: |
npx -p "@getmeli/cli" meli upload docs/.vitepress/dist \ npx -p "@getmeli/cli" meli upload docs/.vitepress/dist \
--url "https://pages.liteyuki.icu" \ --url "https://dash.apage.dev" \
--site "$MELI_SITE" \ --site "$MELI_SITE" \
--token "$MELI_TOKEN" \ --token "$MELI_TOKEN" \
--release "$GITHUB_SHA" --release "$GITHUB_SHA"

View File

@@ -1,6 +1,6 @@
<!--suppress LongLine --> <!--suppress LongLine -->
<div align="center"> <div align="center">
<a href="https://marshoai-docs.pages.liteyuki.icu"><img src="https://marshoai-docs.pages.liteyuki.icu/marsho-full.svg" width="800" height="430" alt="MarshoLogo"></a> <a href="https://marsho.liteyuki.org"><img src="https://marsho.liteyuki.org/marsho-full.svg" width="800" height="430" alt="MarshoLogo"></a>
<br> <br>
</div> </div>
@@ -48,7 +48,7 @@ _谁不喜欢回复消息快又可爱的猫娘呢_
## 😼 使用 ## 😼 使用
请查看[使用文档](https://marshoai-docs.pages.liteyuki.icu/start/use.html) 请查看[使用文档](https://marsho.liteyuki.org/start/use.html)
## ❤ 鸣谢&版权说明 ## ❤ 鸣谢&版权说明

View File

@@ -1,6 +1,6 @@
<!--suppress LongLine --> <!--suppress LongLine -->
<div align="center"> <div align="center">
<a href="https://marshoai-docs.pages.liteyuki.icu"><img src="https://marshoai-docs.pages.liteyuki.icu/marsho-full.svg" width="800" height="430" alt="MarshoLogo"></a> <a href="https://marsho.liteyuki.org"><img src="https://marsho.liteyuki.org/marsho-full.svg" width="800" height="430" alt="MarshoLogo"></a>
<br> <br>
</div> </div>
@@ -48,7 +48,7 @@ Plugin internally installed the catgirl character of Marsho, is able to have a c
- 🐾 Play! I like play with friends! - 🐾 Play! I like play with friends!
## 😼 Usage ## 😼 Usage
Please read [Documentation](https://marshoai-docs.pages.liteyuki.icu/start/use.html) Please read [Documentation](https://marsho.liteyuki.org/start/use.html)
## ❤ Thanks&Copyright ## ❤ Thanks&Copyright
This project uses the following code from other projects: This project uses the following code from other projects:

View File

@@ -65,7 +65,7 @@ When nonebot linked to OneBot v11 adapter, can recieve double click and response
MarshoTools is a feature added in `v0.5.0`, support loading external function library to provide Function Call for Marsho. MarshoTools is a feature added in `v0.5.0`, support loading external function library to provide Function Call for Marsho.
## 🧩 Marsho Plugin ## 🧩 Marsho Plugin
Marsho Plugin is a feature added in `v1.0.0`, replacing the old MarshoTools feature. [Documentation](https://marshoai-docs.pages.liteyuki.icu/dev/extension) Marsho Plugin is a feature added in `v1.0.0`, replacing the old MarshoTools feature. [Documentation](https://marsho.liteyuki.org/dev/extension)
## 👍 Praise list ## 👍 Praise list

View File

@@ -68,7 +68,7 @@ GitHub Models API 的限制较多,不建议使用,建议通过修改`MARSHOA
## 🧩 小棉插件 ## 🧩 小棉插件
小棉插件是`v1.0.0`的新增功能,替代旧的小棉工具功能。[使用文档](https://marshoai-docs.pages.liteyuki.icu/dev/extension) 小棉插件是`v1.0.0`的新增功能,替代旧的小棉工具功能。[使用文档](https://marsho.liteyuki.org/dev/extension)
## 👍 夸赞名单 ## 👍 夸赞名单

View File

@@ -26,17 +26,19 @@ from nonebot.plugin import require
require("nonebot_plugin_alconna") require("nonebot_plugin_alconna")
require("nonebot_plugin_localstore") require("nonebot_plugin_localstore")
require("nonebot_plugin_argot")
import nonebot_plugin_localstore as store # type: ignore import nonebot_plugin_localstore as store # type: ignore
from nonebot import get_driver, logger # type: ignore from nonebot import get_driver, logger # type: ignore
from .config import config from .config import config
# from .hunyuan import *
from .dev import * from .dev import *
from .marsho import * from .marsho import *
from .metadata import metadata from .metadata import metadata
# from .hunyuan import *
__author__ = "Asankilp" __author__ = "Asankilp"
__plugin_meta__ = metadata __plugin_meta__ = metadata

View File

@@ -37,7 +37,7 @@ OPENAI_NEW_MODELS: list = [
INTRODUCTION: str = f"""MarshoAI-NoneBot by LiteyukiStudio INTRODUCTION: str = f"""MarshoAI-NoneBot by LiteyukiStudio
你好喵~我是一只可爱的猫娘AI名叫小棉~🐾! 你好喵~我是一只可爱的猫娘AI名叫小棉~🐾!
我的主页在这里哦~↓↓↓ 我的主页在这里哦~↓↓↓
https://marshoai-docs.pages.liteyuki.icu https://marsho.liteyuki.org
※ 使用 「{config.marshoai_default_name}.status」命令获取状态信息。 ※ 使用 「{config.marshoai_default_name}.status」命令获取状态信息。
※ 使用「{config.marshoai_default_name}.help」命令获取使用说明。""" ※ 使用「{config.marshoai_default_name}.help」命令获取使用说明。"""

View File

@@ -1,15 +1,16 @@
import os import os
from pathlib import Path from pathlib import Path
from nonebot import get_driver, logger, require from nonebot import get_driver, logger, on_command, require
from nonebot.adapters import Bot, Event from nonebot.adapters import Bot, Event
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot_plugin_argot import add_argot, get_message_id
from nonebot_plugin_marshoai.plugin.load import reload_plugin from nonebot_plugin_marshoai.plugin.load import reload_plugin
from .config import config from .config import config
from .marsho import context from .instances import context
from .plugin.func_call.models import SessionContext from .plugin.func_call.models import SessionContext
require("nonebot_plugin_alconna") require("nonebot_plugin_alconna")
@@ -48,6 +49,21 @@ function_call = on_alconna(
permission=SUPERUSER, permission=SUPERUSER,
) )
argot_test = on_command("argot", permission=SUPERUSER)
@argot_test.handle()
async def _():
await argot_test.send(
"aa",
argot={
"name": "test",
"command": "test",
"segment": f"{os.getcwd()}",
"expired_at": 1000,
},
)
@function_call.assign("list") @function_call.assign("list")
async def list_functions(): async def list_functions():
@@ -98,30 +114,38 @@ async def call_function(
recursive=True, recursive=True,
) )
def on_plugin_file_change(event): def on_plugin_file_change(event):
if event.src_path.endswith(".py"): if not event.src_path.endswith(".py"):
logger.info(f"文件变动: {event.src_path}") return
# 层层向上查找到插件目录
dir_list: list[str] = event.src_path.split("/") # type: ignore logger.info(f"文件变动: {event.src_path}")
dir_list[-1] = dir_list[-1].split(".", 1)[0] # 层层向上查找到插件目录
dir_list.reverse() dir_list: list[str] = event.src_path.split("/") # type: ignore
for plugin_name in dir_list: dir_list[-1] = dir_list[-1].split(".", 1)[0]
if plugin := get_plugin(plugin_name): dir_list.reverse()
if plugin.module_path.endswith("__init__.py"):
# 包插件 for plugin_name in dir_list:
if os.path.dirname(plugin.module_path).replace( if not (plugin := get_plugin(plugin_name)):
"\\", "/" continue
) in event.src_path.replace("\\", "/"):
logger.debug(f"找到变动插件: {plugin.name},正在重新加载") if (
reload_plugin(plugin) plugin.module_path
context.reset_all() and plugin.module_path.endswith("__init__.py")
break and os.path.dirname(plugin.module_path).replace("\\", "/")
else: in event.src_path.replace("\\", "/")
# 单文件插件 ): # 插件
if plugin.module_path == event.src_path: logger.debug(f"找到变动插件: {plugin.name},正在重新加载")
logger.debug(f"找到变动插件: {plugin.name},正在重新加载") reload_plugin(plugin)
reload_plugin(plugin) context.reset_all()
context.reset_all() break
break
else: else:
logger.debug("未找到变动插件") # 单文件插件
return if plugin.module_path != event.src_path:
continue
logger.debug(f"找到变动插件: {plugin.name},正在重新加载")
reload_plugin(plugin)
context.reset_all()
break
else:
logger.debug("未找到变动插件")
return

View File

@@ -1,4 +1,5 @@
import json import json
from datetime import timedelta
from typing import Optional, Tuple, Union from typing import Optional, Tuple, Union
from azure.ai.inference.models import ( from azure.ai.inference.models import (
@@ -17,10 +18,16 @@ from nonebot.matcher import (
current_event, current_event,
current_matcher, current_matcher,
) )
from nonebot_plugin_alconna.uniseg import UniMessage, UniMsg from nonebot_plugin_alconna.uniseg import (
Text,
UniMessage,
UniMsg,
get_message_id,
get_target,
)
from nonebot_plugin_argot import Argot # type: ignore
from openai import AsyncOpenAI, AsyncStream from openai import AsyncOpenAI, AsyncStream
from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessage from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessage
from openai.types.chat.chat_completion import Choice
from .config import config from .config import config
from .constants import SUPPORT_IMAGE_MODELS from .constants import SUPPORT_IMAGE_MODELS
@@ -36,6 +43,7 @@ from .util import (
make_chat_openai, make_chat_openai,
parse_richtext, parse_richtext,
) )
from .utils.processor import process_chat_stream, process_completion_to_details
class MarshoHandler: class MarshoHandler:
@@ -50,8 +58,8 @@ class MarshoHandler:
self.event: Event = current_event.get() self.event: Event = current_event.get()
# self.state: T_State = current_handler.get().state # self.state: T_State = current_handler.get().state
self.matcher: Matcher = current_matcher.get() self.matcher: Matcher = current_matcher.get()
self.message_id: str = UniMessage.get_message_id(self.event) self.message_id: str = get_message_id(self.event)
self.target = UniMessage.get_target(self.event) self.target = get_target(self.event)
async def process_user_input( async def process_user_input(
self, user_input: UniMsg, model_name: str self, user_input: UniMsg, model_name: str
@@ -120,7 +128,7 @@ class MarshoHandler:
completion: Union[ChatCompletion, AsyncStream[ChatCompletionChunk]], completion: Union[ChatCompletion, AsyncStream[ChatCompletionChunk]],
user_message: Union[str, list], user_message: Union[str, list],
model_name: str, model_name: str,
tools_list: list, tools_list: list | None = None,
): ):
# function call # function call
# 需要获取额外信息,调用函数工具 # 需要获取额外信息,调用函数工具
@@ -188,7 +196,7 @@ class MarshoHandler:
self, self,
user_message: Union[str, list], user_message: Union[str, list],
model_name: str, model_name: str,
tools_list: list, tools_list: list | None = None,
stream: bool = False, stream: bool = False,
tool_message: Optional[list] = None, tool_message: Optional[list] = None,
) -> Optional[Tuple[UserMessage, ChatCompletionMessage]]: ) -> Optional[Tuple[UserMessage, ChatCompletionMessage]]:
@@ -230,12 +238,28 @@ class MarshoHandler:
target_list.append([self.target.id, self.target.private]) target_list.append([self.target.id, self.target.private])
# 对话成功发送消息 # 对话成功发送消息
send_message = UniMessage()
if config.marshoai_enable_richtext_parse: if config.marshoai_enable_richtext_parse:
await (await parse_richtext(str(choice_msg_content))).send( send_message = await parse_richtext(str(choice_msg_content))
reply_to=True
)
else: else:
await UniMessage(str(choice_msg_content)).send(reply_to=True) send_message = UniMessage(str(choice_msg_content))
send_message.append(
Argot(
"detail",
Text(await process_completion_to_details(response)),
command="detail",
expired_at=timedelta(minutes=5),
) # type:ignore
)
# send_message.append(
# Argot(
# "debug",
# Text(str(response)),
# command=f"debug",
# expired_at=timedelta(minutes=5),
# )
# )
await send_message.send(reply_to=True)
return UserMessage(content=user_message), choice_msg_after return UserMessage(content=user_message), choice_msg_after
elif choice.finish_reason == CompletionsFinishReason.CONTENT_FILTERED: elif choice.finish_reason == CompletionsFinishReason.CONTENT_FILTERED:
@@ -257,9 +281,9 @@ class MarshoHandler:
self, self,
user_message: Union[str, list], user_message: Union[str, list],
model_name: str, model_name: str,
tools_list: list, tools_list: list | None = None,
tools_message: Optional[list] = None, tools_message: Optional[list] = None,
) -> Union[ChatCompletion, None]: ) -> ChatCompletion:
""" """
处理流式请求 处理流式请求
""" """
@@ -272,56 +296,6 @@ class MarshoHandler:
) )
if isinstance(response, AsyncStream): if isinstance(response, AsyncStream):
reasoning_contents = "" return await process_chat_stream(response)
answer_contents = "" else:
last_chunk = None raise TypeError("Unexpected response type for stream request")
is_first_token_appeared = False
is_answering = False
async for chunk in response:
last_chunk = chunk
# print(chunk)
if not is_first_token_appeared:
logger.debug(f"{chunk.id}: 第一个 token 已出现")
is_first_token_appeared = True
if not chunk.choices:
logger.info("Usage:", chunk.usage)
else:
delta = chunk.choices[0].delta
if (
hasattr(delta, "reasoning_content")
and delta.reasoning_content is not None
):
reasoning_contents += delta.reasoning_content
else:
if not is_answering:
logger.debug(
f"{chunk.id}: 思维链已输出完毕或无 reasoning_content 字段输出"
)
is_answering = True
if delta.content is not None:
answer_contents += delta.content
# print(last_chunk)
# 创建新的 ChatCompletion 对象
if last_chunk and last_chunk.choices:
message = ChatCompletionMessage(
content=answer_contents,
role="assistant",
tool_calls=last_chunk.choices[0].delta.tool_calls, # type: ignore
)
if reasoning_contents != "":
setattr(message, "reasoning_content", reasoning_contents)
choice = Choice(
finish_reason=last_chunk.choices[0].finish_reason, # type: ignore
index=last_chunk.choices[0].index,
message=message,
)
return ChatCompletion(
id=last_chunk.id,
choices=[choice],
created=last_chunk.created,
model=last_chunk.model,
system_fingerprint=last_chunk.system_fingerprint,
object="chat.completion",
usage=last_chunk.usage,
)
return None

View File

@@ -15,7 +15,15 @@ from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.rule import to_me from nonebot.rule import to_me
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot_plugin_alconna import MsgTarget, UniMessage, UniMsg, on_alconna from nonebot_plugin_alconna import (
Emoji,
MsgTarget,
UniMessage,
UniMsg,
message_reaction,
on_alconna,
)
from nonebot_plugin_argot.extension import ArgotExtension # type: ignore
from .config import config from .config import config
from .constants import INTRODUCTION, SUPPORT_IMAGE_MODELS from .constants import INTRODUCTION, SUPPORT_IMAGE_MODELS
@@ -25,6 +33,7 @@ from .instances import client, context, model_name, target_list, tools
from .metadata import metadata from .metadata import metadata
from .plugin.func_call.caller import get_function_calls from .plugin.func_call.caller import get_function_calls
from .util import * from .util import *
from .utils.processor import process_chat_stream
async def at_enable(): async def at_enable():
@@ -55,6 +64,7 @@ marsho_cmd = on_alconna(
aliases=tuple(config.marshoai_aliases), aliases=tuple(config.marshoai_aliases),
priority=96, priority=96,
block=True, block=True,
extensions=[ArgotExtension()],
) )
resetmem_cmd = on_alconna( resetmem_cmd = on_alconna(
Alconna( Alconna(
@@ -111,7 +121,7 @@ async def add_assistantmsg(target: MsgTarget, arg: Message = CommandArg()):
@praises_cmd.handle() @praises_cmd.handle()
async def praises(): async def praises():
# await UniMessage(await tools.call("marshoai-weather.get_weather", {"location":"杭州"})).send() # await UniMessage(await tools.call("marshoai-weather.get_weather", {"location":"杭州"})).send()
await praises_cmd.finish(build_praises()) await praises_cmd.finish(await build_praises())
@contexts_cmd.handle() @contexts_cmd.handle()
@@ -226,6 +236,7 @@ async def marsho(
if not text: if not text:
# 发送说明 # 发送说明
# await UniMessage(metadata.usage + "\n当前使用的模型" + model_name).send() # await UniMessage(metadata.usage + "\n当前使用的模型" + model_name).send()
await message_reaction(Emoji("38"))
await marsho_cmd.finish(INTRODUCTION) await marsho_cmd.finish(INTRODUCTION)
backup_context = await get_backup_context(target.id, target.private) backup_context = await get_backup_context(target.id, target.private)
if backup_context: if backup_context:
@@ -256,6 +267,7 @@ async def marsho(
map(lambda v: v.data(), get_function_calls().values()) map(lambda v: v.data(), get_function_calls().values())
) )
logger.info(f"正在获取回答,模型:{model_name}") logger.info(f"正在获取回答,模型:{model_name}")
await message_reaction(Emoji("66"))
# logger.info(f"上下文:{context_msg}") # logger.info(f"上下文:{context_msg}")
response = await handler.handle_common_chat( response = await handler.handle_common_chat(
usermsg, model_name, tools_lists, config.marshoai_stream usermsg, model_name, tools_lists, config.marshoai_stream
@@ -282,19 +294,23 @@ with contextlib.suppress(ImportError): # 优化先不做()
async def poke(event: Event): async def poke(event: Event):
user_nickname = await get_nickname_by_user_id(event.get_user_id()) user_nickname = await get_nickname_by_user_id(event.get_user_id())
usermsg = await get_prompt(model_name) + [
UserMessage(content=f"*{user_nickname}{config.marshoai_poke_suffix}"),
]
try: try:
if config.marshoai_poke_suffix != "": if config.marshoai_poke_suffix != "":
logger.info(f"收到戳一戳,用户昵称:{user_nickname}") logger.info(f"收到戳一戳,用户昵称:{user_nickname}")
response = await make_chat_openai(
pre_response = await make_chat_openai(
client=client, client=client,
model_name=model_name, model_name=model_name,
msg=await get_prompt(model_name) msg=usermsg,
+ [ stream=config.marshoai_stream,
UserMessage(
content=f"*{user_nickname}{config.marshoai_poke_suffix}"
),
],
) )
if isinstance(pre_response, AsyncStream):
response = await process_chat_stream(pre_response)
else:
response = pre_response
choice = response.choices[0] # type: ignore choice = response.choices[0] # type: ignore
if choice.finish_reason == CompletionsFinishReason.STOPPED: if choice.finish_reason == CompletionsFinishReason.STOPPED:
content = extract_content_and_think(choice.message)[0] content = extract_content_and_think(choice.message)[0]

View File

@@ -1,12 +1,12 @@
import importlib import importlib
import importlib.util
import json import json
import os import os
import sys import sys
# import importlib.util
import traceback import traceback
from nonebot import logger from nonebot import logger
from typing_extensions import deprecated
from .config import config from .config import config
@@ -73,6 +73,7 @@ class MarshoContext:
return self._get_target_dict(is_private).setdefault(target_id, []) return self._get_target_dict(is_private).setdefault(target_id, [])
@deprecated("小棉工具已弃用,无法正常调用")
class MarshoTools: class MarshoTools:
""" """
Marsho 的工具类 Marsho 的工具类
@@ -115,10 +116,12 @@ class MarshoTools:
spec = importlib.util.spec_from_file_location( spec = importlib.util.spec_from_file_location(
package_name, os.path.join(package_path, "__init__.py") package_name, os.path.join(package_path, "__init__.py")
) )
if not spec:
raise ImportError(f"工具包 {package_name} 未找到")
package = importlib.util.module_from_spec(spec) package = importlib.util.module_from_spec(spec)
self.imported_packages[package_name] = package self.imported_packages[package_name] = package
sys.modules[package_name] = package sys.modules[package_name] = package
spec.loader.exec_module(package) spec.loader.exec_module(package) # type:ignore
logger.success(f"成功加载工具包 {package_name}") logger.success(f"成功加载工具包 {package_name}")
except json.JSONDecodeError as e: except json.JSONDecodeError as e:

View File

@@ -29,7 +29,7 @@ def debounce(wait):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
nonlocal last_call_time nonlocal last_call_time
current_time = time.time() current_time = time.time()
if (current_time - last_call_time) > wait: if last_call_time is None or (current_time - last_call_time) > wait:
last_call_time = current_time last_call_time = current_time
return func(*args, **kwargs) return func(*args, **kwargs)
@@ -52,7 +52,7 @@ class CodeModifiedHandler(FileSystemEventHandler):
""" """
@debounce(1) @debounce(1)
def on_modified(self, event): def on_modified(self, event: FileSystemEvent):
raise NotImplementedError("on_modified must be implemented") raise NotImplementedError("on_modified must be implemented")
def on_created(self, event): def on_created(self, event):

View File

@@ -18,7 +18,7 @@ from nonebot_plugin_alconna import Text as TextMsg
from nonebot_plugin_alconna import UniMessage from nonebot_plugin_alconna import UniMessage
from openai import AsyncOpenAI, AsyncStream, NotGiven from openai import AsyncOpenAI, AsyncStream, NotGiven
from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessage from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessage
from zhDateTime import DateTime from zhDateTime import DateTime # type: ignore
from ._types import DeveloperMessage from ._types import DeveloperMessage
from .cache.decos import * from .cache.decos import *

View File

@@ -0,0 +1,90 @@
from nonebot.log import logger
from openai import AsyncStream
from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessage
from openai.types.chat.chat_completion import Choice
async def process_chat_stream(
stream: AsyncStream[ChatCompletionChunk],
) -> ChatCompletion:
reasoning_contents = ""
answer_contents = ""
last_chunk = None
is_first_token_appeared = False
is_answering = False
async for chunk in stream:
last_chunk = chunk
# print(chunk)
if not is_first_token_appeared:
logger.info(f"{chunk.id}: 第一个 token 已出现")
is_first_token_appeared = True
if not chunk.choices:
logger.info("Usage:", chunk.usage)
else:
delta = chunk.choices[0].delta
if (
hasattr(delta, "reasoning_content")
and delta.reasoning_content is not None # type:ignore
):
reasoning_contents += delta.reasoning_content # type:ignore
else:
if not is_answering:
logger.info(
f"{chunk.id}: 思维链已输出完毕或无 reasoning_content 字段输出"
)
is_answering = True
if delta.content is not None:
answer_contents += delta.content
# print(last_chunk)
# 创建新的 ChatCompletion 对象
if last_chunk and last_chunk.choices:
message = ChatCompletionMessage(
content=answer_contents,
role="assistant",
tool_calls=last_chunk.choices[0].delta.tool_calls, # type: ignore
)
if reasoning_contents != "":
setattr(message, "reasoning_content", reasoning_contents)
choice = Choice(
finish_reason=last_chunk.choices[0].finish_reason, # type: ignore
index=last_chunk.choices[0].index,
message=message,
)
return ChatCompletion(
id=last_chunk.id,
choices=[choice],
created=last_chunk.created,
model=last_chunk.model,
system_fingerprint=last_chunk.system_fingerprint,
object="chat.completion",
usage=last_chunk.usage,
)
else:
return ChatCompletion(
id="",
choices=[],
created=0,
model="",
system_fingerprint="",
object="chat.completion",
usage=None,
)
async def process_completion_to_details(completion: ChatCompletion) -> str:
if not isinstance(completion, ChatCompletion):
return "暂不支持对流式调用用量的获取,或预期之外的输入"
usage_text = ""
usage = completion.usage
if usage is None:
usage_text = ""
else:
usage_text = str(usage)
details_text = f"""=========消息详情=========
模型: {completion.model}
消息 ID: {completion.id}
用量信息: {usage_text}"""
# print(details_text)
return details_text

32
pdm.lock generated
View File

@@ -5,7 +5,7 @@
groups = ["default", "dev", "test"] groups = ["default", "dev", "test"]
strategy = ["inherit_metadata"] strategy = ["inherit_metadata"]
lock_version = "4.5.0" lock_version = "4.5.0"
content_hash = "sha256:d7ab3d9ca825de512d4f87ec846f7fddcf3d5796a7c9562e60c8c7d39c058817" content_hash = "sha256:6aa043fb1d2d4d384e0d0c698c02a27f22e099828d2973a4baef05c5316f4ee0"
[[metadata.targets]] [[metadata.targets]]
requires_python = "~=3.10" requires_python = "~=3.10"
@@ -1485,7 +1485,7 @@ files = [
[[package]] [[package]]
name = "nonebot-plugin-alconna" name = "nonebot-plugin-alconna"
version = "0.54.1" version = "0.57.0"
requires_python = ">=3.9" requires_python = ">=3.9"
summary = "Alconna Adapter for Nonebot" summary = "Alconna Adapter for Nonebot"
groups = ["default"] groups = ["default"]
@@ -1499,8 +1499,8 @@ dependencies = [
"tarina<0.7,>=0.6.8", "tarina<0.7,>=0.6.8",
] ]
files = [ files = [
{file = "nonebot_plugin_alconna-0.54.1-py3-none-any.whl", hash = "sha256:4edb4b081cd64ce37717c7a92d31aadd2cf287a5a0adc2ac86ed82d9bcad5048"}, {file = "nonebot_plugin_alconna-0.57.0-py3-none-any.whl", hash = "sha256:6c4bcce1a9aa176244b4c011b19b1cea00269c4c6794cd4e90d8dd7990ec3ec9"},
{file = "nonebot_plugin_alconna-0.54.1.tar.gz", hash = "sha256:66fae03120b8eff25bb0027d65f149e399aa6f73c7585ebdd388d1904cecdeee"}, {file = "nonebot_plugin_alconna-0.57.0.tar.gz", hash = "sha256:7a9a4bf373f3f6836611dbde1a0917b84441a534dd6f2b20dae3ba6fff142858"},
] ]
[[package]] [[package]]
@@ -1519,9 +1519,27 @@ files = [
{file = "nonebot_plugin_apscheduler-0.5.0.tar.gz", hash = "sha256:6c0230e99765f275dc83d6639ff33bd6f71203fa10cd1b8a204b0f95530cda86"}, {file = "nonebot_plugin_apscheduler-0.5.0.tar.gz", hash = "sha256:6c0230e99765f275dc83d6639ff33bd6f71203fa10cd1b8a204b0f95530cda86"},
] ]
[[package]]
name = "nonebot-plugin-argot"
version = "0.1.7"
requires_python = ">=3.10"
summary = "NoneBot 暗语"
groups = ["default"]
dependencies = [
"aiofiles>=24.1.0",
"nonebot-plugin-alconna>=0.51.1",
"nonebot-plugin-apscheduler>=0.5.0",
"nonebot-plugin-localstore>=0.7.4",
"nonebot2>=2.3.2",
]
files = [
{file = "nonebot_plugin_argot-0.1.7-py3-none-any.whl", hash = "sha256:1af939a60967e27aff6f7ce97150d26cba8f1ef0cf216b44372cc0d8e5937204"},
{file = "nonebot_plugin_argot-0.1.7.tar.gz", hash = "sha256:f76c2139c9af1e2de6efdc487b728fbad84737d272bf1f600d085bbe6ed79094"},
]
[[package]] [[package]]
name = "nonebot-plugin-localstore" name = "nonebot-plugin-localstore"
version = "0.7.3" version = "0.7.4"
requires_python = "<4.0,>=3.9" requires_python = "<4.0,>=3.9"
summary = "Local Storage Support for NoneBot2" summary = "Local Storage Support for NoneBot2"
groups = ["default"] groups = ["default"]
@@ -1532,8 +1550,8 @@ dependencies = [
"typing-extensions<5.0.0,>=4.0.0", "typing-extensions<5.0.0,>=4.0.0",
] ]
files = [ files = [
{file = "nonebot_plugin_localstore-0.7.3-py3-none-any.whl", hash = "sha256:1bc239b4b5320df0dc08eada7c4f8ba4cb92d4dc3134bf4646ab5e297bd7e575"}, {file = "nonebot_plugin_localstore-0.7.4-py3-none-any.whl", hash = "sha256:3b08030878eadcdd8b9ce3d079da0dc2d0e41dc91f0b2d8cf7fa862a27de9090"},
{file = "nonebot_plugin_localstore-0.7.3.tar.gz", hash = "sha256:1aff10e2dacfc5bc9ce239fd34849f8d7172a118135dbc5aeba1c97605d9959d"}, {file = "nonebot_plugin_localstore-0.7.4.tar.gz", hash = "sha256:85ddc13814bfcd484ab311306823651390020bf44f4fb4733b343a58e72723ce"},
] ]
[[package]] [[package]]

View File

@@ -10,7 +10,7 @@ authors = [
] ]
dependencies = [ dependencies = [
"nonebot2>=2.4.0", "nonebot2>=2.4.0",
"nonebot-plugin-alconna>=0.48.0", "nonebot-plugin-alconna>=0.57.1",
"nonebot-plugin-localstore>=0.7.1", "nonebot-plugin-localstore>=0.7.1",
"zhDatetime>=2.0.0", "zhDatetime>=2.0.0",
"aiohttp>=3.9", "aiohttp>=3.9",
@@ -28,13 +28,14 @@ dependencies = [
"azure-ai-inference>=1.0.0b6", "azure-ai-inference>=1.0.0b6",
"watchdog>=6.0.0", "watchdog>=6.0.0",
"nonebot-plugin-apscheduler>=0.5.0", "nonebot-plugin-apscheduler>=0.5.0",
"openai>=1.58.1" "openai>=1.58.1",
"nonebot-plugin-argot>=0.1.7"
] ]
license = { text = "MIT, Mulan PSL v2" } license = { text = "MIT, Mulan PSL v2" }
[project.urls] [project.urls]
Homepage = "https://marshoai-docs.pages.liteyuki.icu/" Homepage = "https://marsho.liteyuki.org/"
[tool.nonebot] [tool.nonebot]