From 39a5487f39b892a20bb380c15e8485512d4d6034 Mon Sep 17 00:00:00 2001 From: zwssunny Date: Sun, 5 Mar 2023 17:46:35 +0800 Subject: [PATCH 1/3] =?UTF-8?q?openai=20=E6=8E=A5=E5=8F=A3=E8=BF=94?= =?UTF-8?q?=E5=9B=9Etoken=E6=95=B0=E9=87=8F=E6=9D=A5=E4=BF=AE=E5=89=AA?= =?UTF-8?q?=E4=BC=9A=E8=AF=9D=E9=95=BF=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot/chatgpt/chat_gpt_bot.py | 52 +++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/bot/chatgpt/chat_gpt_bot.py b/bot/chatgpt/chat_gpt_bot.py index 7b9012e..f9de246 100644 --- a/bot/chatgpt/chat_gpt_bot.py +++ b/bot/chatgpt/chat_gpt_bot.py @@ -6,7 +6,6 @@ from common.log import logger from common.expired_dict import ExpiredDict import openai import time -import json if conf().get('expires_in_seconds'): user_session = ExpiredDict(conf().get('expires_in_seconds')) @@ -44,12 +43,19 @@ class ChatGPTBot(Bot): logger.debug("[OPEN_AI] new_query={}, user={}, reply_cont={}".format(new_query, from_user_id, reply_content)) if reply_content: Session.save_session(query, reply_content, from_user_id) - return reply_content + return reply_content[1] elif context.get('type', None) == 'IMAGE_CREATE': return self.create_img(query, 0) def reply_text(self, query, user_id, retry_count=0): + ''' + call openai's ChatCompletion to get the answer + :param query: query content + :param user_id: from user id + :param retry_count: retry count + :return: [0]-tokens used and [1]-answer + ''' try: response = openai.ChatCompletion.create( model="gpt-3.5-turbo", # 对话模型的名称 @@ -62,8 +68,8 @@ class ChatGPTBot(Bot): ) # res_content = response.choices[0]['text'].strip().replace('<|endoftext|>', '') logger.info(response.choices[0]['message']['content']) - # log.info("[OPEN_AI] reply={}".format(res_content)) - return response.choices[0]['message']['content'] + + return response["usage"]["prompt_tokens"],response.choices[0]['message']['content'] except openai.error.RateLimitError as e: # rate limit exception logger.warn(e) @@ -72,21 +78,21 @@ class ChatGPTBot(Bot): logger.warn("[OPEN_AI] RateLimit exceed, 第{}次重试".format(retry_count+1)) return self.reply_text(query, user_id, retry_count+1) else: - return "提问太快啦,请休息一下再问我吧" + return 0,"提问太快啦,请休息一下再问我吧" except openai.error.APIConnectionError as e: # api connection exception logger.warn(e) logger.warn("[OPEN_AI] APIConnection failed") - return "我连接不到你的网络" + return 0,"我连接不到你的网络" except openai.error.Timeout as e: logger.warn(e) logger.warn("[OPEN_AI] Timeout") - return "我没有收到你的消息" + return 0,"我没有收到你的消息" except Exception as e: # unknown exception logger.exception(e) Session.clear_session(user_id) - return "请再问我一次吧" + return 0,"请再问我一次吧" def create_img(self, query, retry_count=0): try: @@ -142,31 +148,27 @@ class Session(object): if not max_tokens: # default 3000 max_tokens = 1000 + max_tokens=int(max_tokens) session = user_session.get(user_id) if session: # append conversation - gpt_item = {'role': 'assistant', 'content': answer} + gpt_item = {'role': 'assistant', 'content': answer[1]} session.append(gpt_item) # discard exceed limit conversation - Session.discard_exceed_conversation(user_session[user_id], max_tokens) + used_tokens=int(answer[0]) + # logger.info("prompt tokens used={},max_tokens={}".format(used_tokens,max_tokens)) + + while used_tokens > max_tokens: + # pop first conversation + if len(session) > 0: + session.pop(0) + else: + break + + used_tokens=used_tokens-max_tokens - @staticmethod - def discard_exceed_conversation(session, max_tokens): - count = 0 - count_list = list() - for i in range(len(session)-1, -1, -1): - # count tokens of conversation list - history_conv = session[i] - tokens=json.dumps(history_conv).split() - count += len(tokens) - count_list.append(count) - - for c in count_list: - if c > max_tokens: - # pop first conversation - session.pop(0) @staticmethod def clear_session(user_id): From 7ad16731fdace63719741229177add377f5dae10 Mon Sep 17 00:00:00 2001 From: zwssunny Date: Sun, 5 Mar 2023 17:54:00 +0800 Subject: [PATCH 2/3] =?UTF-8?q?scripts/=E7=9B=AE=E5=BD=95=E6=9C=89?= =?UTF-8?q?=E7=9B=B8=E5=BA=94=E7=9A=84=E8=84=9A=E6=9C=AC=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E6=9C=8D=E5=8A=A1=E5=99=A8=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3948efc..a750378 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ touch nohup.out # 首次运行需要新建日 nohup python3 app.py & tail -f nohup.out # 在后台运行程序并通过日志输出二维码 ``` 扫码登录后程序即可运行于服务器后台,此时可通过 `ctrl+c` 关闭日志,不会影响后台程序的运行。使用 `ps -ef | grep app.py | grep -v grep` 命令可查看运行于后台的进程,如果想要重新启动程序可以先 `kill` 掉对应的进程。日志关闭后如果想要再次打开只需输入 `tail -f nohup.out`。 +scripts/目录有相应的脚本可以调用 > **注意:** 如果 扫码后手机提示登录验证需要等待5s,而终端的二维码再次刷新并提示 `Log in time out, reloading QR code`,此时需参考此 [issue](https://github.com/zhayujie/chatgpt-on-wechat/issues/8) 修改一行代码即可解决。 From 5de600c68927e20c1b13fc8d99ec959bae131760 Mon Sep 17 00:00:00 2001 From: zwssunny Date: Sun, 5 Mar 2023 22:15:15 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=BC=9A=E8=AF=9Dtokens?= =?UTF-8?q?=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot/chatgpt/chat_gpt_bot.py | 42 +++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/bot/chatgpt/chat_gpt_bot.py b/bot/chatgpt/chat_gpt_bot.py index f9de246..9909f74 100644 --- a/bot/chatgpt/chat_gpt_bot.py +++ b/bot/chatgpt/chat_gpt_bot.py @@ -40,21 +40,21 @@ class ChatGPTBot(Bot): # return self.reply_text_stream(query, new_query, from_user_id) reply_content = self.reply_text(new_query, from_user_id, 0) - logger.debug("[OPEN_AI] new_query={}, user={}, reply_cont={}".format(new_query, from_user_id, reply_content)) - if reply_content: - Session.save_session(query, reply_content, from_user_id) - return reply_content[1] + logger.debug("[OPEN_AI] new_query={}, user={}, reply_cont={}".format(new_query, from_user_id, reply_content["content"])) + if reply_content["completion_tokens"] > 0: + Session.save_session(reply_content["content"], from_user_id, reply_content["total_tokens"]) + return reply_content["content"] elif context.get('type', None) == 'IMAGE_CREATE': return self.create_img(query, 0) - def reply_text(self, query, user_id, retry_count=0): + def reply_text(self, query, user_id, retry_count=0) ->dict: ''' call openai's ChatCompletion to get the answer :param query: query content :param user_id: from user id :param retry_count: retry count - :return: [0]-tokens used and [1]-answer + :return: {} ''' try: response = openai.ChatCompletion.create( @@ -68,8 +68,9 @@ class ChatGPTBot(Bot): ) # res_content = response.choices[0]['text'].strip().replace('<|endoftext|>', '') logger.info(response.choices[0]['message']['content']) - - return response["usage"]["prompt_tokens"],response.choices[0]['message']['content'] + return {"total_tokens": response["usage"]["total_tokens"], + "completion_tokens": response["usage"]["completion_tokens"], + "content": response.choices[0]['message']['content']} except openai.error.RateLimitError as e: # rate limit exception logger.warn(e) @@ -78,21 +79,21 @@ class ChatGPTBot(Bot): logger.warn("[OPEN_AI] RateLimit exceed, 第{}次重试".format(retry_count+1)) return self.reply_text(query, user_id, retry_count+1) else: - return 0,"提问太快啦,请休息一下再问我吧" + return {"completion_tokens": 0, "content": "提问太快啦,请休息一下再问我吧"} except openai.error.APIConnectionError as e: # api connection exception logger.warn(e) logger.warn("[OPEN_AI] APIConnection failed") - return 0,"我连接不到你的网络" + return {"completion_tokens": 0, "content":"我连接不到你的网络"} except openai.error.Timeout as e: logger.warn(e) logger.warn("[OPEN_AI] Timeout") - return 0,"我没有收到你的消息" + return {"completion_tokens": 0, "content":"我没有收到你的消息"} except Exception as e: # unknown exception logger.exception(e) Session.clear_session(user_id) - return 0,"请再问我一次吧" + return {"completion_tokens": 0, "content": "请再问我一次吧"} def create_img(self, query, retry_count=0): try: @@ -143,7 +144,7 @@ class Session(object): return session @staticmethod - def save_session(query, answer, user_id): + def save_session(answer, user_id, total_tokens): max_tokens = conf().get("conversation_max_tokens") if not max_tokens: # default 3000 @@ -153,22 +154,23 @@ class Session(object): session = user_session.get(user_id) if session: # append conversation - gpt_item = {'role': 'assistant', 'content': answer[1]} + gpt_item = {'role': 'assistant', 'content': answer} session.append(gpt_item) # discard exceed limit conversation - used_tokens=int(answer[0]) - # logger.info("prompt tokens used={},max_tokens={}".format(used_tokens,max_tokens)) + Session.discard_exceed_conversation(session, max_tokens, total_tokens) - while used_tokens > max_tokens: + @staticmethod + def discard_exceed_conversation(session, max_tokens, total_tokens): + dec_tokens=int(total_tokens) + # logger.info("prompt tokens used={},max_tokens={}".format(used_tokens,max_tokens)) + while dec_tokens > max_tokens: # pop first conversation if len(session) > 0: session.pop(0) else: break - - used_tokens=used_tokens-max_tokens - + dec_tokens=dec_tokens-max_tokens @staticmethod def clear_session(user_id):