diff --git a/config-template.py b/config-template.py index 987e63da..c3e6490c 100644 --- a/config-template.py +++ b/config-template.py @@ -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 diff --git a/override-all.json b/override-all.json index e46e1c90..738264b1 100644 --- a/override-all.json +++ b/override-all.json @@ -65,6 +65,7 @@ "image_api_params": { "size": "256x256" }, + "trace_function_calls": false, "quote_origin": true, "at_sender": false, "include_image_description": true, diff --git a/pkg/openai/api/chat_completion.py b/pkg/openai/api/chat_completion.py index 4c375f7e..032e14bc 100644 --- a/pkg/openai/api/chat_completion.py +++ b/pkg/openai/api/chat_completion.py @@ -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" diff --git a/pkg/openai/modelmgr.py b/pkg/openai/modelmgr.py index 0b287752..cdc51297 100644 --- a/pkg/openai/modelmgr.py +++ b/pkg/openai/modelmgr.py @@ -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: diff --git a/pkg/openai/session.py b/pkg/openai/session.py index 1ab3fd88..6bf13dca 100644 --- a/pkg/openai/session.py +++ b/pkg/openai/session.py @@ -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': diff --git a/pkg/qqbot/cmds/aamgr.py b/pkg/qqbot/cmds/aamgr.py index 2f535102..cfc95b5a 100644 --- a/pkg/qqbot/cmds/aamgr.py +++ b/pkg/qqbot/cmds/aamgr.py @@ -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'])) diff --git a/pkg/qqbot/cmds/session/continue.py b/pkg/qqbot/cmds/session/continue.py deleted file mode 100644 index 6f5cc45b..00000000 --- a/pkg/qqbot/cmds/session/continue.py +++ /dev/null @@ -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 diff --git a/pkg/qqbot/message.py b/pkg/qqbot/message.py index 354c818a..8fe168ae 100644 --- a/pkg/qqbot/message.py +++ b/pkg/qqbot/message.py @@ -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失败,请重试或联系管理员,或等待修复") diff --git a/res/templates/cmdpriv-template.json b/res/templates/cmdpriv-template.json index e0944bda..33603878 100644 --- a/res/templates/cmdpriv-template.json +++ b/res/templates/cmdpriv-template.json @@ -8,7 +8,6 @@ "plugin.del": 2, "plugin.off": 2, "plugin.on": 2, - "continue": 1, "default": 1, "default.set": 2, "del": 1, diff --git a/res/wiki/功能使用.md b/res/wiki/功能使用.md index 67b7ce89..df469bcf 100644 --- a/res/wiki/功能使用.md +++ b/res/wiki/功能使用.md @@ -180,7 +180,6 @@ !draw <提示语> 进行绘图 !version 查看当前版本并检查更新 !resend 重新回复上一个问题 -!continue 继续响应未完成的回合(通常用于内容函数继续调用) !plugin 用法请查看插件使用页的`管理`章节 !default 查看可用的情景预设值 ```