mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-08 14:56:03 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6919bece77 | ||
|
|
8b003739f1 | ||
|
|
2e9229a6ad | ||
|
|
5a3e7fe8ee | ||
|
|
7b3d7e7bd6 | ||
|
|
fdd7c1864d | ||
|
|
cac5a5adff | ||
|
|
63307633c2 | ||
|
|
387dfa39ff | ||
|
|
1f797f899c | ||
|
|
092bb0a1e2 | ||
|
|
2c3399e237 | ||
|
|
835275b47f | ||
|
|
7b060ce3f9 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -34,4 +34,4 @@ bard.json
|
|||||||
res/instance_id.json
|
res/instance_id.json
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/data
|
/data
|
||||||
botpy.log
|
botpy.log*
|
||||||
15
README.md
15
README.md
@@ -11,10 +11,8 @@
|
|||||||
<a href="https://hub.docker.com/repository/docker/rockchin/qchatgpt">
|
<a href="https://hub.docker.com/repository/docker/rockchin/qchatgpt">
|
||||||
<img src="https://img.shields.io/docker/pulls/rockchin/qchatgpt?color=blue" alt="docker pull">
|
<img src="https://img.shields.io/docker/pulls/rockchin/qchatgpt?color=blue" alt="docker pull">
|
||||||
</a>
|
</a>
|
||||||

|

|
||||||
<a href="https://codecov.io/gh/RockChinQ/QChatGPT" >
|

|
||||||
<img src="https://codecov.io/gh/RockChinQ/QChatGPT/graph/badge.svg?token=pjxYIL2kbC"/>
|
|
||||||
</a>
|
|
||||||
<br/>
|
<br/>
|
||||||
<img src="https://img.shields.io/badge/python-3.9 | 3.10 | 3.11-blue.svg" alt="python">
|
<img src="https://img.shields.io/badge/python-3.9 | 3.10 | 3.11-blue.svg" alt="python">
|
||||||
<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=66-aWvn8cbP4c1ut_1YYkvvGVeEtyTH8&authKey=pTaKBK5C%2B8dFzQ4XlENf6MHTCLaHnlKcCRx7c14EeVVlpX2nRSaS8lJm8YeM4mCU&noverify=0&group_code=195992197">
|
<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=66-aWvn8cbP4c1ut_1YYkvvGVeEtyTH8&authKey=pTaKBK5C%2B8dFzQ4XlENf6MHTCLaHnlKcCRx7c14EeVVlpX2nRSaS8lJm8YeM4mCU&noverify=0&group_code=195992197">
|
||||||
@@ -23,12 +21,9 @@
|
|||||||
<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=nC80H57wmKPwRDLFeQrDDjVl81XuC21P&authKey=2wTUTfoQ5v%2BD4C5zfpuR%2BSPMDqdXgDXA%2FS2wHI1NxTfWIG%2B%2FqK08dgyjMMOzhXa9&noverify=0&group_code=248432104">
|
<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=nC80H57wmKPwRDLFeQrDDjVl81XuC21P&authKey=2wTUTfoQ5v%2BD4C5zfpuR%2BSPMDqdXgDXA%2FS2wHI1NxTfWIG%2B%2FqK08dgyjMMOzhXa9&noverify=0&group_code=248432104">
|
||||||
<img alt="Static Badge" src="https://img.shields.io/badge/%E7%A4%BE%E5%8C%BA%E7%BE%A4-248432104-purple">
|
<img alt="Static Badge" src="https://img.shields.io/badge/%E7%A4%BE%E5%8C%BA%E7%BE%A4-248432104-purple">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://www.bilibili.com/video/BV14h4y1w7TC">
|
<a href="https://codecov.io/gh/RockChinQ/QChatGPT" >
|
||||||
<img alt="Static Badge" src="https://img.shields.io/badge/%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B-208647">
|
<img src="https://codecov.io/gh/RockChinQ/QChatGPT/graph/badge.svg?token=pjxYIL2kbC"/>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://www.bilibili.com/video/BV11h4y1y74H">
|
|
||||||
<img alt="Static Badge" src="https://img.shields.io/badge/Linux%E9%83%A8%E7%BD%B2%E8%A7%86%E9%A2%91-208647">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
## 使用文档
|
## 使用文档
|
||||||
|
|
||||||
|
|||||||
29
main.py
29
main.py
@@ -1,5 +1,6 @@
|
|||||||
# QChatGPT 终端启动入口
|
# QChatGPT 终端启动入口
|
||||||
# 在此层级解决依赖项检查。
|
# 在此层级解决依赖项检查。
|
||||||
|
# QChatGPT/main.py
|
||||||
|
|
||||||
asciiart = r"""
|
asciiart = r"""
|
||||||
___ ___ _ _ ___ ___ _____
|
___ ___ _ _ ___ ___ _____
|
||||||
@@ -49,6 +50,34 @@ async def main_entry():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
import os
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
if os.name == 'nt':
|
||||||
|
allowed_parent_process = ['cmd.exe', 'powershell.exe', 'wsl.exe']
|
||||||
|
|
||||||
|
parent_process = psutil.Process(os.getppid()).name()
|
||||||
|
|
||||||
|
if parent_process not in allowed_parent_process:
|
||||||
|
print("请在命令行中运行此程序。")
|
||||||
|
input("按任意键退出...")
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
# 检查本目录是否有main.py,且包含QChatGPT字符串
|
||||||
|
invalid_pwd = False
|
||||||
|
|
||||||
|
if not os.path.exists('main.py'):
|
||||||
|
invalid_pwd = True
|
||||||
|
else:
|
||||||
|
with open('main.py', 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
if "QChatGPT/main.py" not in content:
|
||||||
|
invalid_pwd = True
|
||||||
|
if invalid_pwd:
|
||||||
|
print("请在QChatGPT项目根目录下运行此程序。")
|
||||||
|
input("按任意键退出...")
|
||||||
|
exit(0)
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
asyncio.run(main_entry())
|
asyncio.run(main_entry())
|
||||||
|
|||||||
@@ -19,7 +19,13 @@ class JSONConfigFile(file_model.ConfigFile):
|
|||||||
return os.path.exists(self.config_file_name)
|
return os.path.exists(self.config_file_name)
|
||||||
|
|
||||||
async def create(self):
|
async def create(self):
|
||||||
shutil.copyfile(self.template_file_name, self.config_file_name)
|
if self.template_file_name is not None:
|
||||||
|
shutil.copyfile(self.template_file_name, self.config_file_name)
|
||||||
|
elif self.template_data is not None:
|
||||||
|
with open(self.config_file_name, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(self.template_data, f, indent=4, ensure_ascii=False)
|
||||||
|
else:
|
||||||
|
raise ValueError("template_file_name or template_data must be provided")
|
||||||
|
|
||||||
async def load(self) -> dict:
|
async def load(self) -> dict:
|
||||||
|
|
||||||
@@ -27,12 +33,11 @@ class JSONConfigFile(file_model.ConfigFile):
|
|||||||
await self.create()
|
await self.create()
|
||||||
|
|
||||||
if self.template_file_name is not None:
|
if self.template_file_name is not None:
|
||||||
with open(self.config_file_name, "r", encoding="utf-8") as f:
|
with open(self.template_file_name, "r", encoding="utf-8") as f:
|
||||||
cfg = json.load(f)
|
self.template_data = json.load(f)
|
||||||
|
|
||||||
# 从模板文件中进行补全
|
with open(self.config_file_name, "r", encoding="utf-8") as f:
|
||||||
with open(self.template_file_name, "r", encoding="utf-8") as f:
|
cfg = json.load(f)
|
||||||
self.template_data = json.load(f)
|
|
||||||
|
|
||||||
for key in self.template_data:
|
for key in self.template_data:
|
||||||
if key not in cfg:
|
if key not in cfg:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class SensitiveWordMigration(migration.Migration):
|
|||||||
async def need_migrate(self) -> bool:
|
async def need_migrate(self) -> bool:
|
||||||
"""判断当前环境是否需要运行此迁移
|
"""判断当前环境是否需要运行此迁移
|
||||||
"""
|
"""
|
||||||
return os.path.exists("data/config/sensitive-words.json")
|
return os.path.exists("data/config/sensitive-words.json") and not os.path.exists("data/metadata/sensitive-words.json")
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
"""执行迁移
|
"""执行迁移
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ class Conversation(pydantic.BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class Session(pydantic.BaseModel):
|
class Session(pydantic.BaseModel):
|
||||||
"""会话,一个 Session 对应一个 {launcher_type}_{launcher_id}"""
|
"""会话,一个 Session 对应一个 {launcher_type.value}_{launcher_id}"""
|
||||||
launcher_type: LauncherTypes
|
launcher_type: LauncherTypes
|
||||||
|
|
||||||
launcher_id: int
|
launcher_id: int
|
||||||
|
|||||||
@@ -25,22 +25,24 @@ class BanSessionCheckStage(stage.PipelineStage):
|
|||||||
|
|
||||||
sess_list = self.ap.pipeline_cfg.data['access-control'][mode]
|
sess_list = self.ap.pipeline_cfg.data['access-control'][mode]
|
||||||
|
|
||||||
if (query.launcher_type == 'group' and 'group_*' in sess_list) \
|
if (query.launcher_type.value == 'group' and 'group_*' in sess_list) \
|
||||||
or (query.launcher_type == 'person' and 'person_*' in sess_list):
|
or (query.launcher_type.value == 'person' and 'person_*' in sess_list):
|
||||||
found = True
|
found = True
|
||||||
else:
|
else:
|
||||||
for sess in sess_list:
|
for sess in sess_list:
|
||||||
if sess == f"{query.launcher_type}_{query.launcher_id}":
|
if sess == f"{query.launcher_type.value}_{query.launcher_id}":
|
||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
|
|
||||||
result = False
|
ctn = False
|
||||||
|
|
||||||
if mode == 'blacklist':
|
if mode == 'whitelist':
|
||||||
result = found
|
ctn = found
|
||||||
|
else:
|
||||||
|
ctn = not found
|
||||||
|
|
||||||
return entities.StageProcessResult(
|
return entities.StageProcessResult(
|
||||||
result_type=entities.ResultType.CONTINUE if not result else entities.ResultType.INTERRUPT,
|
result_type=entities.ResultType.CONTINUE if ctn else entities.ResultType.INTERRUPT,
|
||||||
new_query=query,
|
new_query=query,
|
||||||
debug_notice=f'根据访问控制忽略消息: {query.launcher_type}_{query.launcher_id}' if result else ''
|
console_notice=f'根据访问控制忽略消息: {query.launcher_type.value}_{query.launcher_id}' if not ctn else ''
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class ContentFilterStage(stage.PipelineStage):
|
|||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
|
|
||||||
filters_required = [
|
filters_required = [
|
||||||
"content-filter"
|
"content-ignore",
|
||||||
]
|
]
|
||||||
|
|
||||||
if self.ap.pipeline_cfg.data['check-sensitive-words']:
|
if self.ap.pipeline_cfg.data['check-sensitive-words']:
|
||||||
@@ -135,9 +135,17 @@ class ContentFilterStage(stage.PipelineStage):
|
|||||||
query
|
query
|
||||||
)
|
)
|
||||||
elif stage_inst_name == 'PostContentFilterStage':
|
elif stage_inst_name == 'PostContentFilterStage':
|
||||||
return await self._post_process(
|
# 仅处理 query.resp_messages[-1].content 是 str 的情况
|
||||||
query.resp_messages[-1].content,
|
if isinstance(query.resp_messages[-1].content, str):
|
||||||
query
|
return await self._post_process(
|
||||||
)
|
query.resp_messages[-1].content,
|
||||||
|
query
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.ap.logger.debug(f"resp_messages[-1] 不是 str 类型,跳过内容过滤器检查。")
|
||||||
|
return entities.StageProcessResult(
|
||||||
|
result_type=entities.ResultType.CONTINUE,
|
||||||
|
new_query=query
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f'未知的 stage_inst_name: {stage_inst_name}')
|
raise ValueError(f'未知的 stage_inst_name: {stage_inst_name}')
|
||||||
|
|||||||
@@ -56,8 +56,19 @@ class LongTextProcessStage(stage.PipelineStage):
|
|||||||
await self.strategy_impl.initialize()
|
await self.strategy_impl.initialize()
|
||||||
|
|
||||||
async def process(self, query: core_entities.Query, stage_inst_name: str) -> entities.StageProcessResult:
|
async def process(self, query: core_entities.Query, stage_inst_name: str) -> entities.StageProcessResult:
|
||||||
if len(str(query.resp_message_chain)) > self.ap.platform_cfg.data['long-text-process']['threshold']:
|
# 检查是否包含非 Plain 组件
|
||||||
|
contains_non_plain = False
|
||||||
|
|
||||||
|
for msg in query.resp_message_chain:
|
||||||
|
if not isinstance(msg, Plain):
|
||||||
|
contains_non_plain = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if contains_non_plain:
|
||||||
|
self.ap.logger.debug("消息中包含非 Plain 组件,跳过长消息处理。")
|
||||||
|
elif len(str(query.resp_message_chain)) > self.ap.platform_cfg.data['long-text-process']['threshold']:
|
||||||
query.resp_message_chain = MessageChain(await self.strategy_impl.process(str(query.resp_message_chain), query))
|
query.resp_message_chain = MessageChain(await self.strategy_impl.process(str(query.resp_message_chain), query))
|
||||||
|
|
||||||
return entities.StageProcessResult(
|
return entities.StageProcessResult(
|
||||||
result_type=entities.ResultType.CONTINUE,
|
result_type=entities.ResultType.CONTINUE,
|
||||||
new_query=query
|
new_query=query
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class ChatMessageHandler(handler.MessageHandler):
|
|||||||
query.resp_messages.append(
|
query.resp_messages.append(
|
||||||
llm_entities.Message(
|
llm_entities.Message(
|
||||||
role='plugin',
|
role='plugin',
|
||||||
content=str(mc),
|
content=mc,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class RateLimit(stage.PipelineStage):
|
|||||||
)
|
)
|
||||||
elif stage_inst_name == "ReleaseRateLimitOccupancy":
|
elif stage_inst_name == "ReleaseRateLimitOccupancy":
|
||||||
await self.algo.release_access(
|
await self.algo.release_access(
|
||||||
query.launcher_type,
|
query.launcher_type.value,
|
||||||
query.launcher_id,
|
query.launcher_id,
|
||||||
)
|
)
|
||||||
return entities.StageProcessResult(
|
return entities.StageProcessResult(
|
||||||
|
|||||||
@@ -34,7 +34,10 @@ class ResponseWrapper(stage.PipelineStage):
|
|||||||
new_query=query
|
new_query=query
|
||||||
)
|
)
|
||||||
elif query.resp_messages[-1].role == 'plugin':
|
elif query.resp_messages[-1].role == 'plugin':
|
||||||
query.resp_message_chain = mirai.MessageChain(query.resp_messages[-1].content)
|
if not isinstance(query.resp_messages[-1].content, mirai.MessageChain):
|
||||||
|
query.resp_message_chain = mirai.MessageChain(query.resp_messages[-1].content)
|
||||||
|
else:
|
||||||
|
query.resp_message_chain = query.resp_messages[-1].content
|
||||||
|
|
||||||
yield entities.StageProcessResult(
|
yield entities.StageProcessResult(
|
||||||
result_type=entities.ResultType.CONTINUE,
|
result_type=entities.ResultType.CONTINUE,
|
||||||
|
|||||||
@@ -368,11 +368,15 @@ class OfficialAdapter(adapter_model.MessageSourceAdapter):
|
|||||||
member_openid_mapping: OpenIDMapping[str, int] = None
|
member_openid_mapping: OpenIDMapping[str, int] = None
|
||||||
group_openid_mapping: OpenIDMapping[str, int] = None
|
group_openid_mapping: OpenIDMapping[str, int] = None
|
||||||
|
|
||||||
|
group_msg_seq = None
|
||||||
|
|
||||||
def __init__(self, cfg: dict, ap: app.Application):
|
def __init__(self, cfg: dict, ap: app.Application):
|
||||||
"""初始化适配器"""
|
"""初始化适配器"""
|
||||||
self.cfg = cfg
|
self.cfg = cfg
|
||||||
self.ap = ap
|
self.ap = ap
|
||||||
|
|
||||||
|
self.group_msg_seq = 1
|
||||||
|
|
||||||
switchs = {}
|
switchs = {}
|
||||||
|
|
||||||
for intent in cfg["intents"]:
|
for intent in cfg["intents"]:
|
||||||
@@ -419,8 +423,6 @@ class OfficialAdapter(adapter_model.MessageSourceAdapter):
|
|||||||
|
|
||||||
message_list = self.message_converter.yiri2target(message)
|
message_list = self.message_converter.yiri2target(message)
|
||||||
|
|
||||||
msg_seq = 1
|
|
||||||
|
|
||||||
for msg in message_list:
|
for msg in message_list:
|
||||||
args = {}
|
args = {}
|
||||||
|
|
||||||
@@ -462,8 +464,8 @@ class OfficialAdapter(adapter_model.MessageSourceAdapter):
|
|||||||
args["msg_id"] = cached_message_ids[
|
args["msg_id"] = cached_message_ids[
|
||||||
str(message_source.message_chain.message_id)
|
str(message_source.message_chain.message_id)
|
||||||
]
|
]
|
||||||
args["msg_seq"] = msg_seq
|
args["msg_seq"] = self.group_msg_seq
|
||||||
msg_seq += 1
|
self.group_msg_seq += 1
|
||||||
await self.bot.api.post_group_message(**args)
|
await self.bot.api.post_group_message(**args)
|
||||||
|
|
||||||
async def is_muted(self, group_id: int) -> bool:
|
async def is_muted(self, group_id: int) -> bool:
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import typing
|
|||||||
import enum
|
import enum
|
||||||
import pydantic
|
import pydantic
|
||||||
|
|
||||||
|
import mirai
|
||||||
|
|
||||||
|
|
||||||
class FunctionCall(pydantic.BaseModel):
|
class FunctionCall(pydantic.BaseModel):
|
||||||
name: str
|
name: str
|
||||||
@@ -28,7 +30,7 @@ class Message(pydantic.BaseModel):
|
|||||||
name: typing.Optional[str] = None
|
name: typing.Optional[str] = None
|
||||||
"""名称,仅函数调用返回时设置"""
|
"""名称,仅函数调用返回时设置"""
|
||||||
|
|
||||||
content: typing.Optional[str] = None
|
content: typing.Optional[str] | typing.Optional[mirai.MessageChain] = None
|
||||||
"""内容"""
|
"""内容"""
|
||||||
|
|
||||||
function_call: typing.Optional[FunctionCall] = None
|
function_call: typing.Optional[FunctionCall] = None
|
||||||
@@ -41,7 +43,7 @@ class Message(pydantic.BaseModel):
|
|||||||
|
|
||||||
def readable_str(self) -> str:
|
def readable_str(self) -> str:
|
||||||
if self.content is not None:
|
if self.content is not None:
|
||||||
return self.content
|
return str(self.content)
|
||||||
elif self.function_call is not None:
|
elif self.function_call is not None:
|
||||||
return f'{self.function_call.name}({self.function_call.arguments})'
|
return f'{self.function_call.name}({self.function_call.arguments})'
|
||||||
elif self.tool_calls is not None:
|
elif self.tool_calls is not None:
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class AnthropicMessages(api.LLMAPIRequester):
|
|||||||
args["model"] = query.use_model.name if query.use_model.model_name is None else query.use_model.model_name
|
args["model"] = query.use_model.name if query.use_model.model_name is None else query.use_model.model_name
|
||||||
|
|
||||||
req_messages = [ # req_messages 仅用于类内,外部同步由 query.messages 进行
|
req_messages = [ # req_messages 仅用于类内,外部同步由 query.messages 进行
|
||||||
m.dict(exclude_none=True) for m in query.prompt.messages
|
m.dict(exclude_none=True) for m in query.prompt.messages if m.content.strip() != ""
|
||||||
] + [m.dict(exclude_none=True) for m in query.messages]
|
] + [m.dict(exclude_none=True) for m in query.messages]
|
||||||
|
|
||||||
# 删除所有 role=system & content='' 的消息
|
# 删除所有 role=system & content='' 的消息
|
||||||
|
|||||||
@@ -93,11 +93,12 @@ class OpenAIChatCompletions(api.LLMAPIRequester):
|
|||||||
pending_tool_calls = []
|
pending_tool_calls = []
|
||||||
|
|
||||||
req_messages = [ # req_messages 仅用于类内,外部同步由 query.messages 进行
|
req_messages = [ # req_messages 仅用于类内,外部同步由 query.messages 进行
|
||||||
m.dict(exclude_none=True) for m in query.prompt.messages
|
m.dict(exclude_none=True) for m in query.prompt.messages if m.content.strip() != ""
|
||||||
] + [m.dict(exclude_none=True) for m in query.messages]
|
] + [m.dict(exclude_none=True) for m in query.messages]
|
||||||
|
|
||||||
# req_messages.append({"role": "user", "content": str(query.message_chain)})
|
# req_messages.append({"role": "user", "content": str(query.message_chain)})
|
||||||
|
|
||||||
|
# 首次请求
|
||||||
msg = await self._closure(req_messages, query.use_model, query.use_funcs)
|
msg = await self._closure(req_messages, query.use_model, query.use_funcs)
|
||||||
|
|
||||||
yield msg
|
yield msg
|
||||||
@@ -106,23 +107,36 @@ class OpenAIChatCompletions(api.LLMAPIRequester):
|
|||||||
|
|
||||||
req_messages.append(msg.dict(exclude_none=True))
|
req_messages.append(msg.dict(exclude_none=True))
|
||||||
|
|
||||||
|
# 持续请求,只要还有待处理的工具调用就继续处理调用
|
||||||
while pending_tool_calls:
|
while pending_tool_calls:
|
||||||
for tool_call in pending_tool_calls:
|
for tool_call in pending_tool_calls:
|
||||||
func = tool_call.function
|
try:
|
||||||
|
func = tool_call.function
|
||||||
|
|
||||||
parameters = json.loads(func.arguments)
|
parameters = json.loads(func.arguments)
|
||||||
|
|
||||||
func_ret = await self.ap.tool_mgr.execute_func_call(
|
func_ret = await self.ap.tool_mgr.execute_func_call(
|
||||||
query, func.name, parameters
|
query, func.name, parameters
|
||||||
)
|
)
|
||||||
|
|
||||||
msg = llm_entities.Message(
|
msg = llm_entities.Message(
|
||||||
role="tool", content=json.dumps(func_ret, ensure_ascii=False), tool_call_id=tool_call.id
|
role="tool", content=json.dumps(func_ret, ensure_ascii=False), tool_call_id=tool_call.id
|
||||||
)
|
)
|
||||||
|
|
||||||
yield msg
|
yield msg
|
||||||
|
|
||||||
req_messages.append(msg.dict(exclude_none=True))
|
req_messages.append(msg.dict(exclude_none=True))
|
||||||
|
except Exception as e:
|
||||||
|
# 出错,添加一个报错信息到 req_messages
|
||||||
|
err_msg = llm_entities.Message(
|
||||||
|
role="tool", content=f"err: {e}", tool_call_id=tool_call.id
|
||||||
|
)
|
||||||
|
|
||||||
|
yield err_msg
|
||||||
|
|
||||||
|
req_messages.append(
|
||||||
|
err_msg.dict(exclude_none=True)
|
||||||
|
)
|
||||||
|
|
||||||
# 处理完所有调用,继续请求
|
# 处理完所有调用,继续请求
|
||||||
msg = await self._closure(req_messages, query.use_model, query.use_funcs)
|
msg = await self._closure(req_messages, query.use_model, query.use_funcs)
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
semantic_version = "v3.1.0"
|
semantic_version = "v3.1.0.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user