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
+5
View File
@@ -239,6 +239,11 @@ image_api_params = {
"size": "256x256", # 图片尺寸,支持256x256, 512x512, 1024x1024 "size": "256x256", # 图片尺寸,支持256x256, 512x512, 1024x1024
} }
# 跟踪函数调用
# 为True时,在每次GPT进行Function Calling时都会输出发送一条回复给用户
# 同时,一次提问内所有的Function Calling和普通回复消息都会单独发送给用户
trace_function_calls = False
# 群内回复消息时是否引用原消息 # 群内回复消息时是否引用原消息
quote_origin = True quote_origin = True
+1
View File
@@ -65,6 +65,7 @@
"image_api_params": { "image_api_params": {
"size": "256x256" "size": "256x256"
}, },
"trace_function_calls": false,
"quote_origin": true, "quote_origin": true,
"at_sender": false, "at_sender": false,
"include_image_description": true, "include_image_description": true,
+11 -7
View File
@@ -30,7 +30,7 @@ class ChatCompletionRequest(RequestBase):
) )
self.pending_msg = "" 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 = { msg = {
"role": role, "role": role,
"content": content "content": content
@@ -39,6 +39,9 @@ class ChatCompletionRequest(RequestBase):
if name is not None: if name is not None:
msg['name'] = name msg['name'] = name
if function_call is not None:
msg['function_call'] = function_call
self.messages.append(msg) self.messages.append(msg)
def __init__( def __init__(
@@ -87,16 +90,17 @@ class ChatCompletionRequest(RequestBase):
choice0 = resp["choices"][0] choice0 = resp["choices"][0]
# 如果不是函数调用,且finish_reason为stop,则停止迭代 # 如果不是函数调用,且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 self.stopped = True
if 'function_call' in choice0['message']: if 'function_call' in choice0['message']:
self.pending_func_call = choice0['message']['function_call'] self.pending_func_call = choice0['message']['function_call']
# self.append_message( self.append_message(
# role="assistant", role="assistant",
# content="function call: "+json.dumps(self.pending_func_call, ensure_ascii=False) content=choice0['message']['content'],
# ) function_call=choice0['message']['function_call']
)
return { return {
"id": resp["id"], "id": resp["id"],
@@ -106,7 +110,7 @@ class ChatCompletionRequest(RequestBase):
"message": { "message": {
"role": "assistant", "role": "assistant",
"type": "function_call", "type": "function_call",
"content": None, "content": choice0['message']['content'],
"function_call": choice0['message']['function_call'] "function_call": choice0['message']['function_call']
}, },
"finish_reason": "function_call" "finish_reason": "function_call"
+1
View File
@@ -112,6 +112,7 @@ def count_completion_tokens(messages: list, model: str) -> int:
def count_tokens(messages: list, model: str): def count_tokens(messages: list, model: str):
if model in CHAT_COMPLETION_MODELS: if model in CHAT_COMPLETION_MODELS:
return count_chat_completion_tokens(messages, model) return count_chat_completion_tokens(messages, model)
elif model in COMPLETION_MODELS: elif model in COMPLETION_MODELS:
+40 -10
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中添加一条消息,返回接口回复 """向session中添加一条消息,返回接口回复
Args: Args:
@@ -255,29 +255,59 @@ class Session:
funcs = [] 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): 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'] finish_reason = resp['choices'][0]['finish_reason']
if resp['choices'][0]['message']['type'] == 'text': # 普通回复 if resp['choices'][0]['message']['role'] == "assistant" and resp['choices'][0]['message']['content'] != None: # 包含纯文本响应
res_text += resp['choices'][0]['message']['content']
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'] total_tokens += resp['usage']['total_tokens']
pending_msgs.append( msg = {
{ "role": "assistant",
"role": "assistant", "content": resp['choices'][0]['message']['content']
"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( # self.prompt.append(
# { # {
# "role": "assistant", # "role": "assistant",
# "content": "function call: "+json.dumps(resp['choices'][0]['message']['function_call']) # "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'] total_tokens += resp['usage']['total_tokens']
elif resp['choices'][0]['message']['type'] == 'function_return': elif resp['choices'][0]['message']['type'] == 'function_return':
+4
View File
@@ -327,6 +327,10 @@ def apply_privileges():
for path, priv in data.items(): for path, priv in data.items():
if path == 'comment': if path == 'comment':
continue continue
if path not in __command_list__:
continue
if __command_list__[path]['privilege'] != priv: if __command_list__[path]['privilege'] != priv:
logging.debug('应用权限: {} -> {}(default: {})'.format(path, priv, __command_list__[path]['privilege'])) logging.debug('应用权限: {} -> {}(default: {})'.format(path, priv, __command_list__[path]['privilege']))
-27
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
+2 -2
View File
@@ -40,7 +40,7 @@ def process_normal_message(text_message: str, mgr, config, launcher_type: str,
try: try:
prefix = "[GPT]" if config.show_prefix else "" prefix = "[GPT]" if config.show_prefix else ""
text, finish_reason, funcs = session.append(text_message) text, finish_reason, funcs = session.query(text_message)
# 触发插件事件 # 触发插件事件
args = { args = {
@@ -68,7 +68,7 @@ def process_normal_message(text_message: str, mgr, config, launcher_type: str,
except openai.error.APIConnectionError as e: except openai.error.APIConnectionError as e:
err_msg = str(e) err_msg = str(e)
if err_msg.__contains__('Error communicating with OpenAI'): 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失败,请重试或联系管理员,或等待修复") "[bot]err:调用API失败,请重试或联系管理员,或等待修复")
else: else:
reply = handle_exception("{}会话调用API失败:{}".format(session_name, e), "[bot]err:调用API失败,请重试或联系管理员,或等待修复") reply = handle_exception("{}会话调用API失败:{}".format(session_name, e), "[bot]err:调用API失败,请重试或联系管理员,或等待修复")
-1
View File
@@ -8,7 +8,6 @@
"plugin.del": 2, "plugin.del": 2,
"plugin.off": 2, "plugin.off": 2,
"plugin.on": 2, "plugin.on": 2,
"continue": 1,
"default": 1, "default": 1,
"default.set": 2, "default.set": 2,
"del": 1, "del": 1,
-1
View File
@@ -180,7 +180,6 @@
!draw <提示语> 进行绘图 !draw <提示语> 进行绘图
!version 查看当前版本并检查更新 !version 查看当前版本并检查更新
!resend 重新回复上一个问题 !resend 重新回复上一个问题
!continue 继续响应未完成的回合(通常用于内容函数继续调用)
!plugin 用法请查看插件使用页的`管理`章节 !plugin 用法请查看插件使用页的`管理`章节
!default 查看可用的情景预设值 !default 查看可用的情景预设值
``` ```