Merge pull request #533 from RockChinQ/perf-function-call-process

[Perf] 优化函数调用的底层逻辑
This commit is contained in:
Junyan Qin
2023-08-06 14:52:01 +08:00
committed by GitHub
10 changed files with 64 additions and 48 deletions

View File

@@ -239,6 +239,11 @@ image_api_params = {
"size": "256x256", # 图片尺寸支持256x256, 512x512, 1024x1024
}
# 跟踪函数调用
# 为True时在每次GPT进行Function Calling时都会输出发送一条回复给用户
# 同时一次提问内所有的Function Calling和普通回复消息都会单独发送给用户
trace_function_calls = False
# 群内回复消息时是否引用原消息
quote_origin = True

View File

@@ -65,6 +65,7 @@
"image_api_params": {
"size": "256x256"
},
"trace_function_calls": false,
"quote_origin": true,
"at_sender": false,
"include_image_description": true,

View File

@@ -30,7 +30,7 @@ class ChatCompletionRequest(RequestBase):
)
self.pending_msg = ""
def append_message(self, role: str, content: str, name: str=None):
def append_message(self, role: str, content: str, name: str=None, function_call: dict=None):
msg = {
"role": role,
"content": content
@@ -39,6 +39,9 @@ class ChatCompletionRequest(RequestBase):
if name is not None:
msg['name'] = name
if function_call is not None:
msg['function_call'] = function_call
self.messages.append(msg)
def __init__(
@@ -87,16 +90,17 @@ class ChatCompletionRequest(RequestBase):
choice0 = resp["choices"][0]
# 如果不是函数调用且finish_reason为stop则停止迭代
if 'function_call' not in choice0['message']: # and choice0["finish_reason"] == "stop"
if choice0['finish_reason'] == 'stop': # and choice0["finish_reason"] == "stop"
self.stopped = True
if 'function_call' in choice0['message']:
self.pending_func_call = choice0['message']['function_call']
# self.append_message(
# role="assistant",
# content="function call: "+json.dumps(self.pending_func_call, ensure_ascii=False)
# )
self.append_message(
role="assistant",
content=choice0['message']['content'],
function_call=choice0['message']['function_call']
)
return {
"id": resp["id"],
@@ -106,7 +110,7 @@ class ChatCompletionRequest(RequestBase):
"message": {
"role": "assistant",
"type": "function_call",
"content": None,
"content": choice0['message']['content'],
"function_call": choice0['message']['function_call']
},
"finish_reason": "function_call"

View File

@@ -112,6 +112,7 @@ def count_completion_tokens(messages: list, model: str) -> int:
def count_tokens(messages: list, model: str):
if model in CHAT_COMPLETION_MODELS:
return count_chat_completion_tokens(messages, model)
elif model in COMPLETION_MODELS:

View File

@@ -194,7 +194,7 @@ class Session:
# 请求回复
# 这个函数是阻塞的
def append(self, text: str=None) -> tuple[str, str, list[str]]:
def query(self, text: str=None) -> tuple[str, str, list[str]]:
"""向session中添加一条消息返回接口回复
Args:
@@ -255,29 +255,59 @@ class Session:
funcs = []
trace_func_calls = config.trace_function_calls
botmgr = pkg.utils.context.get_qqbot_manager()
session_name_spt: list[str] = self.name.split("_")
pending_res_text = ""
# TODO 对不起,我知道这样非常非常屎山,但我之后会重构的
for resp in pkg.utils.context.get_openai_manager().request_completion(prompts):
if pending_res_text != "":
botmgr.adapter.send_message(
session_name_spt[0],
session_name_spt[1],
pending_res_text
)
pending_res_text = ""
finish_reason = resp['choices'][0]['finish_reason']
if resp['choices'][0]['message']['type'] == 'text': # 普通回复
res_text += resp['choices'][0]['message']['content']
if resp['choices'][0]['message']['role'] == "assistant" and resp['choices'][0]['message']['content'] != None: # 包含纯文本响应
if not trace_func_calls:
res_text += resp['choices'][0]['message']['content'] + "\n"
else:
res_text = resp['choices'][0]['message']['content']
pending_res_text = resp['choices'][0]['message']['content']
total_tokens += resp['usage']['total_tokens']
pending_msgs.append(
{
"role": "assistant",
"content": resp['choices'][0]['message']['content']
}
)
msg = {
"role": "assistant",
"content": resp['choices'][0]['message']['content']
}
elif resp['choices'][0]['message']['type'] == 'function_call':
if 'function_call' in resp['choices'][0]['message']:
msg['function_call'] = json.dumps(resp['choices'][0]['message']['function_call'])
pending_msgs.append(msg)
if resp['choices'][0]['message']['type'] == 'function_call':
# self.prompt.append(
# {
# "role": "assistant",
# "content": "function call: "+json.dumps(resp['choices'][0]['message']['function_call'])
# }
# )
if trace_func_calls:
botmgr.adapter.send_message(
session_name_spt[0],
session_name_spt[1],
"调用函数 "+resp['choices'][0]['message']['function_call']['name'] + "..."
)
total_tokens += resp['usage']['total_tokens']
elif resp['choices'][0]['message']['type'] == 'function_return':

View File

@@ -327,6 +327,10 @@ def apply_privileges():
for path, priv in data.items():
if path == 'comment':
continue
if path not in __command_list__:
continue
if __command_list__[path]['privilege'] != priv:
logging.debug('应用权限: {} -> {}(default: {})'.format(path, priv, __command_list__[path]['privilege']))

View File

@@ -1,27 +0,0 @@
from ..aamgr import AbstractCommandNode, Context
@AbstractCommandNode.register(
parent=None,
name="continue",
description="继续未完成的响应",
usage="!continue",
aliases=[],
privilege=1
)
class ContinueCommand(AbstractCommandNode):
@classmethod
def process(cls, ctx: Context) -> tuple[bool, list]:
import pkg.openai.session
import config
session_name = ctx.session_name
reply = []
session = pkg.openai.session.get_session(session_name)
text, _, _ = session.append()
reply = [text]
return True, reply

View File

@@ -40,7 +40,7 @@ def process_normal_message(text_message: str, mgr, config, launcher_type: str,
try:
prefix = "[GPT]" if config.show_prefix else ""
text, finish_reason, funcs = session.append(text_message)
text, finish_reason, funcs = session.query(text_message)
# 触发插件事件
args = {
@@ -68,7 +68,7 @@ def process_normal_message(text_message: str, mgr, config, launcher_type: str,
except openai.error.APIConnectionError as e:
err_msg = str(e)
if err_msg.__contains__('Error communicating with OpenAI'):
reply = handle_exception("{}会话调用API失败:{}\n请尝试关闭网络代理来解决此问题。".format(session_name, e),
reply = handle_exception("{}会话调用API失败:{}\n您的网络无法访问OpenAI接口或网络代理不正常".format(session_name, e),
"[bot]err:调用API失败请重试或联系管理员或等待修复")
else:
reply = handle_exception("{}会话调用API失败:{}".format(session_name, e), "[bot]err:调用API失败请重试或联系管理员或等待修复")

View File

@@ -8,7 +8,6 @@
"plugin.del": 2,
"plugin.off": 2,
"plugin.on": 2,
"continue": 1,
"default": 1,
"default.set": 2,
"del": 1,

View File

@@ -180,7 +180,6 @@
!draw <提示语> 进行绘图
!version 查看当前版本并检查更新
!resend 重新回复上一个问题
!continue 继续响应未完成的回合(通常用于内容函数继续调用)
!plugin 用法请查看插件使用页的`管理`章节
!default 查看可用的情景预设值
```