From 0f6e3e2dbbae5872f240fe72db109594c5468888 Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 08:06:46 +0800 Subject: [PATCH 01/15] Fix the issue of ineffective transfer of reference history --- crazy_functions/读文章写摘要.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crazy_functions/读文章写摘要.py b/crazy_functions/读文章写摘要.py index 72ffe6b..bc3beba 100644 --- a/crazy_functions/读文章写摘要.py +++ b/crazy_functions/读文章写摘要.py @@ -1,11 +1,12 @@ -from toolbox import update_ui from toolbox import CatchException, report_execption, write_results_to_file +from toolbox import update_ui from .crazy_utils import request_gpt_model_in_new_thread_with_ui_alive + fast_debug = False def 解析Paper(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt): - import time, glob, os + import time, os print('begin analysis on:', file_manifest) for index, fp in enumerate(file_manifest): with open(fp, 'r', encoding='utf-8', errors='replace') as f: @@ -45,10 +46,11 @@ def 解析Paper(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbo yield from update_ui(chatbot=chatbot, history=history, msg=msg) # 刷新界面 - @CatchException -def 读文章写摘要(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port): - history = [] # 清空历史,以免输入溢出 +def 读文章写摘要(txt, llm_kwargs, plugin_kwargs, chatbot, system_prompt, web_port, history=None): + # history = [] # 清空历史,以免输入溢出 + if history is None: + history = [] # 清空历史,以免输入溢出 import glob, os if os.path.exists(txt): project_folder = txt From bf9731e9376043f787dc71de42bcbd555726c851 Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 08:07:50 +0800 Subject: [PATCH 02/15] Fix the issue of PEP 8: E401 multiple imports on one line --- crazy_functions/读文章写摘要.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crazy_functions/读文章写摘要.py b/crazy_functions/读文章写摘要.py index bc3beba..d9ceb72 100644 --- a/crazy_functions/读文章写摘要.py +++ b/crazy_functions/读文章写摘要.py @@ -51,7 +51,8 @@ def 读文章写摘要(txt, llm_kwargs, plugin_kwargs, chatbot, system_prompt, w # history = [] # 清空历史,以免输入溢出 if history is None: history = [] # 清空历史,以免输入溢出 - import glob, os + import glob + import os if os.path.exists(txt): project_folder = txt else: From 3c38fad4aad96c1f46a027126c6d9fac71ae7c7d Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 08:14:21 +0800 Subject: [PATCH 03/15] PEP 8: E251 unexpected spaces around keyword / parameter equals --- crazy_functions/读文章写摘要.py | 44 +++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/crazy_functions/读文章写摘要.py b/crazy_functions/读文章写摘要.py index d9ceb72..044eb37 100644 --- a/crazy_functions/读文章写摘要.py +++ b/crazy_functions/读文章写摘要.py @@ -12,38 +12,44 @@ def 解析Paper(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbo with open(fp, 'r', encoding='utf-8', errors='replace') as f: file_content = f.read() - prefix = "接下来请你逐文件分析下面的论文文件,概括其内容" if index==0 else "" + prefix = "接下来请你逐文件分析下面的论文文件,概括其内容" if index == 0 else "" i_say = prefix + f'请对下面的文章片段用中文做一个概述,文件名是{os.path.relpath(fp, project_folder)},文章内容是 ```{file_content}```' i_say_show_user = prefix + f'[{index}/{len(file_manifest)}] 请对下面的文章片段做一个概述: {os.path.abspath(fp)}' chatbot.append((i_say_show_user, "[Local Message] waiting gpt response.")) - yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 - if not fast_debug: + if not fast_debug: msg = '正常' # ** gpt request ** - gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(i_say, i_say_show_user, llm_kwargs, chatbot, history=[], sys_prompt=system_prompt) # 带超时倒计时 + gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(i_say, i_say_show_user, llm_kwargs, + chatbot, history=[], + sys_prompt=system_prompt) # 带超时倒计时 chatbot[-1] = (i_say_show_user, gpt_say) - history.append(i_say_show_user); history.append(gpt_say) - yield from update_ui(chatbot=chatbot, history=history, msg=msg) # 刷新界面 + history.append(i_say_show_user); + history.append(gpt_say) + yield from update_ui(chatbot=chatbot, history=history, msg=msg) # 刷新界面 if not fast_debug: time.sleep(2) all_file = ', '.join([os.path.relpath(fp, project_folder) for index, fp in enumerate(file_manifest)]) i_say = f'根据以上你自己的分析,对全文进行概括,用学术性语言写一段中文摘要,然后再写一段英文摘要(包括{all_file})。' chatbot.append((i_say, "[Local Message] waiting gpt response.")) - yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 - if not fast_debug: + if not fast_debug: msg = '正常' # ** gpt request ** - gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(i_say, i_say, llm_kwargs, chatbot, history=history, sys_prompt=system_prompt) # 带超时倒计时 + gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(i_say, i_say, llm_kwargs, chatbot, + history=history, + sys_prompt=system_prompt) # 带超时倒计时 chatbot[-1] = (i_say, gpt_say) - history.append(i_say); history.append(gpt_say) - yield from update_ui(chatbot=chatbot, history=history, msg=msg) # 刷新界面 + history.append(i_say); + history.append(gpt_say) + yield from update_ui(chatbot=chatbot, history=history, msg=msg) # 刷新界面 res = write_results_to_file(history) chatbot.append(("完成了吗?", res)) - yield from update_ui(chatbot=chatbot, history=history, msg=msg) # 刷新界面 + yield from update_ui(chatbot=chatbot, history=history, msg=msg) # 刷新界面 @CatchException @@ -57,14 +63,14 @@ def 读文章写摘要(txt, llm_kwargs, plugin_kwargs, chatbot, system_prompt, w project_folder = txt else: if txt == "": txt = '空空如也的输入栏' - report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到本地项目或无权访问: {txt}") - yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 + report_execption(chatbot, history, a=f"解析项目: {txt}", b=f"找不到本地项目或无权访问: {txt}") + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 return - file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.tex', recursive=True)] # + \ - # [f for f in glob.glob(f'{project_folder}/**/*.cpp', recursive=True)] + \ - # [f for f in glob.glob(f'{project_folder}/**/*.c', recursive=True)] + file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.tex', recursive=True)] # + \ + # [f for f in glob.glob(f'{project_folder}/**/*.cpp', recursive=True)] + \ + # [f for f in glob.glob(f'{project_folder}/**/*.c', recursive=True)] if len(file_manifest) == 0: - report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到任何.tex文件: {txt}") - yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 + report_execption(chatbot, history, a=f"解析项目: {txt}", b=f"找不到任何.tex文件: {txt}") + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 return yield from 解析Paper(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt) From a75ae327e7b8fe57d3fb671cda14303dfe8f1de5 Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 08:23:56 +0800 Subject: [PATCH 04/15] Make it comply with the PEP8 standard and improve PEP 8: E401 multiple imports on one line and PEP 8: E701 multiple statements on one line (colon) --- crazy_functions/读文章写摘要.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crazy_functions/读文章写摘要.py b/crazy_functions/读文章写摘要.py index 044eb37..2edf83a 100644 --- a/crazy_functions/读文章写摘要.py +++ b/crazy_functions/读文章写摘要.py @@ -6,7 +6,8 @@ fast_debug = False def 解析Paper(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt): - import time, os + import time + import os print('begin analysis on:', file_manifest) for index, fp in enumerate(file_manifest): with open(fp, 'r', encoding='utf-8', errors='replace') as f: @@ -44,7 +45,7 @@ def 解析Paper(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbo sys_prompt=system_prompt) # 带超时倒计时 chatbot[-1] = (i_say, gpt_say) - history.append(i_say); + history.append(i_say) history.append(gpt_say) yield from update_ui(chatbot=chatbot, history=history, msg=msg) # 刷新界面 res = write_results_to_file(history) @@ -62,7 +63,8 @@ def 读文章写摘要(txt, llm_kwargs, plugin_kwargs, chatbot, system_prompt, w if os.path.exists(txt): project_folder = txt else: - if txt == "": txt = '空空如也的输入栏' + if txt == "": + txt = '空空如也的输入栏' report_execption(chatbot, history, a=f"解析项目: {txt}", b=f"找不到本地项目或无权访问: {txt}") yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 return From a019a64e65e4d101c2e1e0eefb9b99ae9e176608 Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 08:26:54 +0800 Subject: [PATCH 05/15] PEP 8: E302 expected 2 blank lines, found 1 --- toolbox.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/toolbox.py b/toolbox.py index 18915d0..0beb424 100644 --- a/toolbox.py +++ b/toolbox.py @@ -20,6 +20,7 @@ from functools import wraps, lru_cache ======================================================================== """ + class ChatBotWithCookies(list): def __init__(self, cookie): self._cookies = cookie @@ -70,6 +71,7 @@ def update_ui(chatbot, history, msg='正常', **kwargs): # 刷新界面 assert isinstance(chatbot, ChatBotWithCookies), "在传递chatbot的过程中不要将其丢弃。必要时,可用clear将其清空,然后用for+append循环重新赋值。" yield chatbot.get_cookies(), chatbot, history, msg + def update_ui_lastest_msg(lastmsg, chatbot, history, delay=1): # 刷新界面 """ 刷新用户界面 From 0fc8f740d08b0e317da22735f2834af571bad688 Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 08:29:26 +0800 Subject: [PATCH 06/15] Fix PEP 8: E302 expected 2 blank lines, found 1 & PEP 8: E303 too many blank lines (4) --- toolbox.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/toolbox.py b/toolbox.py index 0beb424..ddfe34c 100644 --- a/toolbox.py +++ b/toolbox.py @@ -90,11 +90,11 @@ def trimmed_format_exc(): replace_path = "." return str.replace(current_path, replace_path) + def CatchException(f): """ 装饰器函数,捕捉函数f中的异常并封装到一个生成器中返回,并显示到聊天当中。 """ - @wraps(f) def decorated(txt, top_p, temperature, chatbot, history, systemPromptTxt, WEB_PORT=-1): try: @@ -149,6 +149,7 @@ def HotReload(f): ======================================================================== """ + def get_reduce_token_percent(text): """ * 此函数未来将被弃用 @@ -208,8 +209,6 @@ def regular_txt_to_markdown(text): return text - - def report_execption(chatbot, history, a, b): """ 向chatbot中添加错误信息 @@ -234,6 +233,7 @@ def text_divide_paragraph(text): text = "
".join(lines) return text + @lru_cache(maxsize=128) # 使用 lru缓存 加快转换速度 def markdown_convertion(txt): """ @@ -433,6 +433,7 @@ def find_recent_files(directory): return recent_files + def promote_file_to_downloadzone(file, rename_file=None): # 将文件复制一份到下载区 import shutil @@ -441,6 +442,7 @@ def promote_file_to_downloadzone(file, rename_file=None): if os.path.exists(new_path): os.remove(new_path) shutil.copyfile(file, new_path) + def on_file_uploaded(files, chatbot, txt, txt2, checkboxes): """ 当文件被上传时的回调函数 @@ -488,17 +490,20 @@ def on_report_generated(files, chatbot): chatbot.append(['报告如何远程获取?', '报告已经添加到右侧“文件上传区”(可能处于折叠状态),请查收。']) return report_files, chatbot + def is_openai_api_key(key): API_MATCH_ORIGINAL = re.match(r"sk-[a-zA-Z0-9]{48}$", key) API_MATCH_AZURE = re.match(r"[a-zA-Z0-9]{32}$", key) return bool(API_MATCH_ORIGINAL) or bool(API_MATCH_AZURE) + def is_api2d_key(key): if key.startswith('fk') and len(key) == 41: return True else: return False + def is_any_api_key(key): if ',' in key: keys = key.split(',') @@ -508,6 +513,7 @@ def is_any_api_key(key): else: return is_openai_api_key(key) or is_api2d_key(key) + def what_keys(keys): avail_key_list = {'OpenAI Key':0, "API2D Key":0} key_list = keys.split(',') @@ -522,6 +528,7 @@ def what_keys(keys): return f"检测到: OpenAI Key {avail_key_list['OpenAI Key']} 个,API2D Key {avail_key_list['API2D Key']} 个" + def select_api_key(keys, llm_model): import random avail_key_list = [] @@ -541,6 +548,7 @@ def select_api_key(keys, llm_model): api_key = random.choice(avail_key_list) # 随机负载均衡 return api_key + def read_env_variable(arg, default_value): """ 环境变量可以是 `GPT_ACADEMIC_CONFIG`(优先),也可以直接是`CONFIG` @@ -595,6 +603,7 @@ def read_env_variable(arg, default_value): print亮绿(f"[ENV_VAR] 成功读取环境变量{arg}") return r + @lru_cache(maxsize=128) def read_single_conf_with_lru_cache(arg): from colorful import print亮红, print亮绿, print亮蓝 @@ -659,6 +668,7 @@ class DummyWith(): def __exit__(self, exc_type, exc_value, traceback): return + def run_gradio_in_subpath(demo, auth, port, custom_path): """ 把gradio的运行地址更改到指定的二次路径上 @@ -753,6 +763,7 @@ def clip_history(inputs, history, tokenizer, max_token_limit): ======================================================================== """ + def zip_folder(source_folder, dest_folder, zip_name): import zipfile import os @@ -784,15 +795,18 @@ def zip_folder(source_folder, dest_folder, zip_name): print(f"Zip file created at {zip_file}") + def zip_result(folder): import time t = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) zip_folder(folder, './gpt_log/', f'{t}-result.zip') - + + def gen_time_str(): import time return time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + class ProxyNetworkActivate(): """ 这段代码定义了一个名为TempProxy的空上下文管理器, 用于给一小段代码上代理 @@ -812,12 +826,14 @@ class ProxyNetworkActivate(): if 'HTTPS_PROXY' in os.environ: os.environ.pop('HTTPS_PROXY') return + def objdump(obj, file='objdump.tmp'): import pickle with open(file, 'wb+') as f: pickle.dump(obj, f) return + def objload(file='objdump.tmp'): import pickle, os if not os.path.exists(file): From 55c6e9c59abb3ed09675868d22a7c5640fd9d802 Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 08:37:37 +0800 Subject: [PATCH 07/15] Specify the proxy input type and use the get method to obtain dictionary data to avoid exceptions. Change the timeout to 30 seconds to avoid failures caused by network fluctuations. Obtain abnormal parameters and display them to the front-end for easy troubleshooting --- check_proxy.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/check_proxy.py b/check_proxy.py index 977802d..ef91b34 100644 --- a/check_proxy.py +++ b/check_proxy.py @@ -1,10 +1,10 @@ -def check_proxy(proxies): +def check_proxy(proxies: dict): import requests - proxies_https = proxies['https'] if proxies is not None else '无' + proxies_https = proxies.get('https') if proxies is not None else '无' try: response = requests.get("https://ipapi.co/json/", - proxies=proxies, timeout=4) + proxies=proxies, timeout=30) data = response.json() print(f'查询代理的地理位置,返回的结果是{data}') if 'country_name' in data: @@ -14,8 +14,8 @@ def check_proxy(proxies): result = f"代理配置 {proxies_https}, 代理所在地:未知,IP查询频率受限" print(result) return result - except: - result = f"代理配置 {proxies_https}, 代理所在地查询超时,代理可能无效" + except Exception as e: + result = f"代理 {proxies_https} 查询出现异常: {e},代理可能无效" print(result) return result From 543a8b98e9b4d7f7a4ad9dabb0495e9974821e81 Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 08:41:55 +0800 Subject: [PATCH 08/15] Local variable 'result' might be referenced before assignment, add else result --- check_proxy.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/check_proxy.py b/check_proxy.py index ef91b34..cabc838 100644 --- a/check_proxy.py +++ b/check_proxy.py @@ -12,6 +12,8 @@ def check_proxy(proxies: dict): result = f"代理配置 {proxies_https}, 代理所在地:{country}" elif 'error' in data: result = f"代理配置 {proxies_https}, 代理所在地:未知,IP查询频率受限" + else: + result = f"代理配置 {proxies_https}, 代理数据解析失败:{data}" print(result) return result except Exception as e: From b538d31b1357b46838f431fc5902b1e3feca4a65 Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 09:07:53 +0800 Subject: [PATCH 09/15] Str is a built-in type and cannot be used directly as a variable name --- toolbox.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/toolbox.py b/toolbox.py index ddfe34c..9f9703f 100644 --- a/toolbox.py +++ b/toolbox.py @@ -84,11 +84,12 @@ def update_ui_lastest_msg(lastmsg, chatbot, history, delay=1): # 刷新界面 def trimmed_format_exc(): - import os, traceback - str = traceback.format_exc() + import os + import traceback + _str = traceback.format_exc() current_path = os.getcwd() replace_path = "." - return str.replace(current_path, replace_path) + return _str.replace(current_path, replace_path) def CatchException(f): @@ -101,7 +102,7 @@ def CatchException(f): yield from f(txt, top_p, temperature, chatbot, history, systemPromptTxt, WEB_PORT) except Exception as e: from check_proxy import check_proxy - from toolbox import get_conf + # from toolbox import get_conf # 不需要导入本文件内容 proxies, = get_conf('proxies') tb_str = '```\n' + trimmed_format_exc() + '```' if len(chatbot) == 0: @@ -109,7 +110,7 @@ def CatchException(f): chatbot.append(["插件调度异常", "异常原因"]) chatbot[-1] = (chatbot[-1][0], f"[Local Message] 实验性函数调用出错: \n\n{tb_str} \n\n当前代理可用性: \n\n{check_proxy(proxies)}") - yield from update_ui(chatbot=chatbot, history=history, msg=f'异常 {e}') # 刷新界面 + yield from update_ui(chatbot=chatbot, history=history, msg=f'异常 {e}') # 刷新界面 return decorated From ffd7363c4c5751d53f5fe1b004cac1ca89ce9fc1 Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 09:13:13 +0800 Subject: [PATCH 10/15] need more detailed and comprehensive exception information, it is usually recommended to use the exception object e. Stack trace information can be used as a supplement to understand the context and call relationship of the exception occurrence --- crazy_functions/crazy_utils.py | 19 ++++++++++++------- toolbox.py | 3 ++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/crazy_functions/crazy_utils.py b/crazy_functions/crazy_utils.py index 96301ff..2944e05 100644 --- a/crazy_functions/crazy_utils.py +++ b/crazy_functions/crazy_utils.py @@ -1,6 +1,7 @@ from toolbox import update_ui, get_conf, trimmed_format_exc import threading + def input_clipping(inputs, history, max_token_limit): import numpy as np from request_llm.bridge_all import model_info @@ -35,6 +36,7 @@ def input_clipping(inputs, history, max_token_limit): history = everything[1:] return inputs, history + def request_gpt_model_in_new_thread_with_ui_alive( inputs, inputs_show_user, llm_kwargs, chatbot, history, sys_prompt, refresh_interval=0.2, @@ -64,9 +66,10 @@ def request_gpt_model_in_new_thread_with_ui_alive( from request_llm.bridge_all import predict_no_ui_long_connection # 用户反馈 chatbot.append([inputs_show_user, ""]) - yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面 + yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面 executor = ThreadPoolExecutor(max_workers=16) mutable = ["", time.time(), ""] + def _req_gpt(inputs, history, sys_prompt): retry_op = retry_times_at_unknown_error exceeded_cnt = 0 @@ -222,15 +225,17 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency( # 【选择放弃】 tb_str = '```\n' + trimmed_format_exc() + '```' gpt_say += f"[Local Message] 警告,线程{index}在执行过程中遭遇问题, Traceback:\n\n{tb_str}\n\n" - if len(mutable[index][0]) > 0: gpt_say += "此线程失败前收到的回答:\n\n" + mutable[index][0] + if len(mutable[index][0]) > 0: + gpt_say += "此线程失败前收到的回答:\n\n" + mutable[index][0] mutable[index][2] = "输入过长已放弃" - return gpt_say # 放弃 - except: + return gpt_say # 放弃 + except Exception as e: # 【第三种情况】:其他错误 tb_str = '```\n' + trimmed_format_exc() + '```' - print(tb_str) + print(f"发生异常:{e}, 调用栈信息:{tb_str}") gpt_say += f"[Local Message] 警告,线程{index}在执行过程中遭遇问题, Traceback:\n\n{tb_str}\n\n" - if len(mutable[index][0]) > 0: gpt_say += "此线程失败前收到的回答:\n\n" + mutable[index][0] + if len(mutable[index][0]) > 0: + gpt_say += "此线程失败前收到的回答:\n\n" + mutable[index][0] if retry_op > 0: retry_op -= 1 wait = random.randint(5, 20) @@ -244,7 +249,7 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency( mutable[index][2] = f"{fail_info}等待重试 {wait-i}"; time.sleep(1) # 开始重试 mutable[index][2] = f"重试中 {retry_times_at_unknown_error-retry_op}/{retry_times_at_unknown_error}" - continue # 返回重试 + continue # 返回重试 else: mutable[index][2] = "已失败" wait = 5 diff --git a/toolbox.py b/toolbox.py index 9f9703f..f16cebf 100644 --- a/toolbox.py +++ b/toolbox.py @@ -76,7 +76,8 @@ def update_ui_lastest_msg(lastmsg, chatbot, history, delay=1): # 刷新界面 """ 刷新用户界面 """ - if len(chatbot) == 0: chatbot.append(["update_ui_last_msg", lastmsg]) + if len(chatbot) == 0: + chatbot.append(["update_ui_last_msg", lastmsg]) chatbot[-1] = list(chatbot[-1]) chatbot[-1][-1] = lastmsg yield from update_ui(chatbot=chatbot, history=history) From 0a83ba91e93084a1efdb023a84271a6f1482b565 Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 09:22:50 +0800 Subject: [PATCH 11/15] Add langchain & For safety reasons, try not to use compilation and installation packages from unknown sources or make changes/ Docs/graphic 3.32.2 py3 none any. whl is graphic>=3.33.1 --- requirements.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1d70323..690718c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -./docs/gradio-3.32.2-py3-none-any.whl +gradio>=3.33.1 tiktoken>=0.3.3 requests[socks] transformers @@ -15,4 +15,5 @@ pymupdf openai numpy arxiv -rich \ No newline at end of file +rich +langchain \ No newline at end of file From 6d7ee17dbd2f67f1cd103756f5e003e333615c4f Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 09:37:04 +0800 Subject: [PATCH 12/15] Add zh_ Langchain into dependent files --- crazy_functions/crazy_utils.py | 201 ++++++++++++++++++--------------- requirements.txt | 3 +- 2 files changed, 111 insertions(+), 93 deletions(-) diff --git a/crazy_functions/crazy_utils.py b/crazy_functions/crazy_utils.py index 2944e05..f3b6e77 100644 --- a/crazy_functions/crazy_utils.py +++ b/crazy_functions/crazy_utils.py @@ -6,12 +6,14 @@ def input_clipping(inputs, history, max_token_limit): import numpy as np from request_llm.bridge_all import model_info enc = model_info["gpt-3.5-turbo"]['tokenizer'] - def get_token_num(txt): return len(enc.encode(txt, disallowed_special=())) + + def get_token_num(txt): + return len(enc.encode(txt, disallowed_special=())) mode = 'input-and-history' # 当 输入部分的token占比 小于 全文的一半时,只裁剪历史 input_token_num = get_token_num(inputs) - if input_token_num < max_token_limit//2: + if input_token_num < max_token_limit // 2: mode = 'only-history' max_token_limit = max_token_limit - input_token_num @@ -19,13 +21,13 @@ def input_clipping(inputs, history, max_token_limit): everything.extend(history) n_token = get_token_num('\n'.join(everything)) everything_token = [get_token_num(e) for e in everything] - delta = max(everything_token) // 16 # 截断时的颗粒度 - + delta = max(everything_token) // 16 # 截断时的颗粒度 + while n_token > max_token_limit: where = np.argmax(everything_token) encoded = enc.encode(everything[where], disallowed_special=()) - clipped_encoded = encoded[:len(encoded)-delta] - everything[where] = enc.decode(clipped_encoded)[:-1] # -1 to remove the may-be illegal char + clipped_encoded = encoded[:len(encoded) - delta] + everything[where] = enc.decode(clipped_encoded)[:-1] # -1 to remove the may-be illegal char everything_token[where] = get_token_num(everything[where]) n_token = get_token_num('\n'.join(everything)) @@ -38,11 +40,11 @@ def input_clipping(inputs, history, max_token_limit): def request_gpt_model_in_new_thread_with_ui_alive( - inputs, inputs_show_user, llm_kwargs, + inputs, inputs_show_user, llm_kwargs, chatbot, history, sys_prompt, refresh_interval=0.2, - handle_token_exceed=True, + handle_token_exceed=True, retry_times_at_unknown_error=2, - ): +): """ Request GPT model,请求GPT模型同时维持用户界面活跃。 @@ -75,7 +77,7 @@ def request_gpt_model_in_new_thread_with_ui_alive( exceeded_cnt = 0 while True: # watchdog error - if len(mutable) >= 2 and (time.time()-mutable[1]) > 5: + if len(mutable) >= 2 and (time.time() - mutable[1]) > 5: raise RuntimeError("检测到程序终止。") try: # 【第一种情况】:顺利完成 @@ -92,14 +94,14 @@ def request_gpt_model_in_new_thread_with_ui_alive( p_ratio, n_exceed = get_reduce_token_percent(str(token_exceeded_error)) MAX_TOKEN = 4096 EXCEED_ALLO = 512 + 512 * exceeded_cnt - inputs, history = input_clipping(inputs, history, max_token_limit=MAX_TOKEN-EXCEED_ALLO) + inputs, history = input_clipping(inputs, history, max_token_limit=MAX_TOKEN - EXCEED_ALLO) mutable[0] += f'[Local Message] 警告,文本过长将进行截断,Token溢出数:{n_exceed}。\n\n' - continue # 返回重试 + continue # 返回重试 else: # 【选择放弃】 tb_str = '```\n' + trimmed_format_exc() + '```' mutable[0] += f"[Local Message] 警告,在执行过程中遭遇问题, Traceback:\n\n{tb_str}\n\n" - return mutable[0] # 放弃 + return mutable[0] # 放弃 except: # 【第三种情况】:其他错误:重试几次 tb_str = '```\n' + trimmed_format_exc() + '```' @@ -107,14 +109,15 @@ def request_gpt_model_in_new_thread_with_ui_alive( mutable[0] += f"[Local Message] 警告,在执行过程中遭遇问题, Traceback:\n\n{tb_str}\n\n" if retry_op > 0: retry_op -= 1 - mutable[0] += f"[Local Message] 重试中,请稍等 {retry_times_at_unknown_error-retry_op}/{retry_times_at_unknown_error}:\n\n" + mutable[ + 0] += f"[Local Message] 重试中,请稍等 {retry_times_at_unknown_error - retry_op}/{retry_times_at_unknown_error}:\n\n" if ("Rate limit reached" in tb_str) or ("Too Many Requests" in tb_str): time.sleep(30) time.sleep(5) - continue # 返回重试 + continue # 返回重试 else: time.sleep(5) - return mutable[0] # 放弃 + return mutable[0] # 放弃 # 提交任务 future = executor.submit(_req_gpt, inputs, history, sys_prompt) @@ -126,21 +129,21 @@ def request_gpt_model_in_new_thread_with_ui_alive( if future.done(): break chatbot[-1] = [chatbot[-1][0], mutable[0]] - yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面 + yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面 final_result = future.result() chatbot[-1] = [chatbot[-1][0], final_result] - yield from update_ui(chatbot=chatbot, history=[]) # 如果最后成功了,则删除报错信息 + yield from update_ui(chatbot=chatbot, history=[]) # 如果最后成功了,则删除报错信息 return final_result def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency( - inputs_array, inputs_show_user_array, llm_kwargs, - chatbot, history_array, sys_prompt_array, + inputs_array, inputs_show_user_array, llm_kwargs, + chatbot, history_array, sys_prompt_array, refresh_interval=0.2, max_workers=-1, scroller_max_len=30, handle_token_exceed=True, show_user_at_complete=False, retry_times_at_unknown_error=2, - ): +): """ Request GPT model using multiple threads with UI and high efficiency 请求GPT模型的[多线程]版。 @@ -173,19 +176,21 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency( from request_llm.bridge_all import predict_no_ui_long_connection assert len(inputs_array) == len(history_array) assert len(inputs_array) == len(sys_prompt_array) - if max_workers == -1: # 读取配置文件 - try: max_workers, = get_conf('DEFAULT_WORKER_NUM') - except: max_workers = 8 + if max_workers == -1: # 读取配置文件 + try: + max_workers, = get_conf('DEFAULT_WORKER_NUM') + except: + max_workers = 8 if max_workers <= 0: max_workers = 3 # 屏蔽掉 chatglm的多线程,可能会导致严重卡顿 if not (llm_kwargs['llm_model'].startswith('gpt-') or llm_kwargs['llm_model'].startswith('api2d-')): max_workers = 1 - + executor = ThreadPoolExecutor(max_workers=max_workers) n_frag = len(inputs_array) # 用户反馈 chatbot.append(["请开始多线程操作。", ""]) - yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面 + yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面 # 跨线程传递 mutable = [["", time.time(), "等待中"] for _ in range(n_frag)] @@ -197,13 +202,13 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency( mutable[index][2] = "执行中" while True: # watchdog error - if len(mutable[index]) >= 2 and (time.time()-mutable[index][1]) > 5: + if len(mutable[index]) >= 2 and (time.time() - mutable[index][1]) > 5: raise RuntimeError("检测到程序终止。") try: # 【第一种情况】:顺利完成 # time.sleep(10); raise RuntimeError("测试") gpt_say = predict_no_ui_long_connection( - inputs=inputs, llm_kwargs=llm_kwargs, history=history, + inputs=inputs, llm_kwargs=llm_kwargs, history=history, sys_prompt=sys_prompt, observe_window=mutable[index], console_slience=True ) mutable[index][2] = "已成功" @@ -217,10 +222,10 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency( p_ratio, n_exceed = get_reduce_token_percent(str(token_exceeded_error)) MAX_TOKEN = 4096 EXCEED_ALLO = 512 + 512 * exceeded_cnt - inputs, history = input_clipping(inputs, history, max_token_limit=MAX_TOKEN-EXCEED_ALLO) + inputs, history = input_clipping(inputs, history, max_token_limit=MAX_TOKEN - EXCEED_ALLO) gpt_say += f'[Local Message] 警告,文本过长将进行截断,Token溢出数:{n_exceed}。\n\n' mutable[index][2] = f"截断重试" - continue # 返回重试 + continue # 返回重试 else: # 【选择放弃】 tb_str = '```\n' + trimmed_format_exc() + '```' @@ -236,7 +241,7 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency( gpt_say += f"[Local Message] 警告,线程{index}在执行过程中遭遇问题, Traceback:\n\n{tb_str}\n\n" if len(mutable[index][0]) > 0: gpt_say += "此线程失败前收到的回答:\n\n" + mutable[index][0] - if retry_op > 0: + if retry_op > 0: retry_op -= 1 wait = random.randint(5, 20) if ("Rate limit reached" in tb_str) or ("Too Many Requests" in tb_str): @@ -246,19 +251,22 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency( fail_info = "" # 也许等待十几秒后,情况会好转 for i in range(wait): - mutable[index][2] = f"{fail_info}等待重试 {wait-i}"; time.sleep(1) + mutable[index][2] = f"{fail_info}等待重试 {wait - i}"; + time.sleep(1) # 开始重试 - mutable[index][2] = f"重试中 {retry_times_at_unknown_error-retry_op}/{retry_times_at_unknown_error}" + mutable[index][ + 2] = f"重试中 {retry_times_at_unknown_error - retry_op}/{retry_times_at_unknown_error}" continue # 返回重试 else: mutable[index][2] = "已失败" wait = 5 time.sleep(5) - return gpt_say # 放弃 + return gpt_say # 放弃 # 异步任务开始 - futures = [executor.submit(_req_gpt, index, inputs, history, sys_prompt) for index, inputs, history, sys_prompt in zip( - range(len(inputs_array)), inputs_array, history_array, sys_prompt_array)] + futures = [executor.submit(_req_gpt, index, inputs, history, sys_prompt) for index, inputs, history, sys_prompt in + zip( + range(len(inputs_array)), inputs_array, history_array, sys_prompt_array)] cnt = 0 while True: # yield一次以刷新前端页面 @@ -272,17 +280,17 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency( mutable[thread_index][1] = time.time() # 在前端打印些好玩的东西 for thread_index, _ in enumerate(worker_done): - print_something_really_funny = "[ ...`"+mutable[thread_index][0][-scroller_max_len:].\ + print_something_really_funny = "[ ...`" + mutable[thread_index][0][-scroller_max_len:]. \ replace('\n', '').replace('```', '...').replace( - ' ', '.').replace('
', '.....').replace('$', '.')+"`... ]" + ' ', '.').replace('
', '.....').replace('$', '.') + "`... ]" observe_win.append(print_something_really_funny) # 在前端打印些好玩的东西 - stat_str = ''.join([f'`{mutable[thread_index][2]}`: {obs}\n\n' - if not done else f'`{mutable[thread_index][2]}`\n\n' + stat_str = ''.join([f'`{mutable[thread_index][2]}`: {obs}\n\n' + if not done else f'`{mutable[thread_index][2]}`\n\n' for thread_index, done, obs in zip(range(len(worker_done)), worker_done, observe_win)]) # 在前端打印些好玩的东西 - chatbot[-1] = [chatbot[-1][0], f'多线程操作已经开始,完成情况: \n\n{stat_str}' + ''.join(['.']*(cnt % 10+1))] - yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面 + chatbot[-1] = [chatbot[-1][0], f'多线程操作已经开始,完成情况: \n\n{stat_str}' + ''.join(['.'] * (cnt % 10 + 1))] + yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面 if all(worker_done): executor.shutdown() break @@ -292,13 +300,13 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency( for inputs_show_user, f in zip(inputs_show_user_array, futures): gpt_res = f.result() gpt_response_collection.extend([inputs_show_user, gpt_res]) - + # 是否在结束时,在界面上显示结果 if show_user_at_complete: for inputs_show_user, f in zip(inputs_show_user_array, futures): gpt_res = f.result() chatbot.append([inputs_show_user, gpt_res]) - yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面 + yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面 time.sleep(0.3) return gpt_response_collection @@ -311,6 +319,7 @@ def breakdown_txt_to_satisfy_token_limit(txt, get_token_fn, limit): lines = txt_tocut.split('\n') estimated_line_cut = limit / get_token_fn(txt_tocut) * len(lines) estimated_line_cut = int(estimated_line_cut) + cnt = 0 for cnt in reversed(range(estimated_line_cut)): if must_break_at_empty_line: if lines[cnt] != "": @@ -327,6 +336,7 @@ def breakdown_txt_to_satisfy_token_limit(txt, get_token_fn, limit): result = [prev] result.extend(cut(post, must_break_at_empty_line)) return result + try: return cut(txt, must_break_at_empty_line=True) except RuntimeError: @@ -342,9 +352,10 @@ def force_breakdown(txt, limit, get_token_fn): return txt[:i], txt[i:] return "Tiktoken未知错误", "Tiktoken未知错误" + def breakdown_txt_to_satisfy_token_limit_for_pdf(txt, get_token_fn, limit): # 递归 - def cut(txt_tocut, must_break_at_empty_line, break_anyway=False): + def cut(txt_tocut, must_break_at_empty_line, break_anyway=False): if get_token_fn(txt_tocut) <= limit: return [txt_tocut] else: @@ -370,6 +381,7 @@ def breakdown_txt_to_satisfy_token_limit_for_pdf(txt, get_token_fn, limit): result = [prev] result.extend(cut(post, must_break_at_empty_line, break_anyway=break_anyway)) return result + try: # 第1次尝试,将双空行(\n\n)作为切分点 return cut(txt, must_break_at_empty_line=True) @@ -380,7 +392,7 @@ def breakdown_txt_to_satisfy_token_limit_for_pdf(txt, get_token_fn, limit): except RuntimeError: try: # 第3次尝试,将英文句号(.)作为切分点 - res = cut(txt.replace('.', '。\n'), must_break_at_empty_line=False) # 这个中文的句号是故意的,作为一个标识而存在 + res = cut(txt.replace('.', '。\n'), must_break_at_empty_line=False) # 这个中文的句号是故意的,作为一个标识而存在 return [r.replace('。\n', '.') for r in res] except RuntimeError as e: try: @@ -392,7 +404,6 @@ def breakdown_txt_to_satisfy_token_limit_for_pdf(txt, get_token_fn, limit): return cut(txt, must_break_at_empty_line=False, break_anyway=True) - def read_and_clean_pdf_text(fp): """ 这个函数用于分割pdf,用了很多trick,逻辑较乱,效果奇好 @@ -420,8 +431,9 @@ def read_and_clean_pdf_text(fp): fc = 0 # Index 0 文本 fs = 1 # Index 1 字体 fb = 2 # Index 2 框框 - REMOVE_FOOT_NOTE = True # 是否丢弃掉 不是正文的内容 (比正文字体小,如参考文献、脚注、图注等) - REMOVE_FOOT_FFSIZE_PERCENT = 0.95 # 小于正文的?时,判定为不是正文(有些文章的正文部分字体大小不是100%统一的,有肉眼不可见的小变化) + REMOVE_FOOT_NOTE = True # 是否丢弃掉 不是正文的内容 (比正文字体小,如参考文献、脚注、图注等) + REMOVE_FOOT_FFSIZE_PERCENT = 0.95 # 小于正文的?时,判定为不是正文(有些文章的正文部分字体大小不是100%统一的,有肉眼不可见的小变化) + def primary_ffsize(l): """ 提取文本块主字体 @@ -431,12 +443,12 @@ def read_and_clean_pdf_text(fp): if wtf['size'] not in fsize_statiscs: fsize_statiscs[wtf['size']] = 0 fsize_statiscs[wtf['size']] += len(wtf['text']) return max(fsize_statiscs, key=fsize_statiscs.get) - - def ffsize_same(a,b): + + def ffsize_same(a, b): """ 提取字体大小是否近似相等 """ - return abs((a-b)/max(a,b)) < 0.02 + return abs((a - b) / max(a, b)) < 0.02 with fitz.open(fp) as doc: meta_txt = [] @@ -456,18 +468,19 @@ def read_and_clean_pdf_text(fp): if len(txt_line) == 0: continue pf = primary_ffsize(l) meta_line.append([txt_line, pf, l['bbox'], l]) - for wtf in l['spans']: # for l in t['lines']: + for wtf in l['spans']: # for l in t['lines']: meta_span.append([wtf['text'], wtf['size'], len(wtf['text'])]) # meta_line.append(["NEW_BLOCK", pf]) - # 块元提取 for each word segment with in line for each line cross-line words for each block + # 块元提取 for each word segment with in line for each line + # cross-line words for each block meta_txt.extend([" ".join(["".join([wtf['text'] for wtf in l['spans']]) for l in t['lines']]).replace( '- ', '') for t in text_areas['blocks'] if 'lines' in t]) meta_font.extend([np.mean([np.mean([wtf['size'] for wtf in l['spans']]) - for l in t['lines']]) for t in text_areas['blocks'] if 'lines' in t]) + for l in t['lines']]) for t in text_areas['blocks'] if 'lines' in t]) if index == 0: page_one_meta = [" ".join(["".join([wtf['text'] for wtf in l['spans']]) for l in t['lines']]).replace( '- ', '') for t in text_areas['blocks'] if 'lines' in t] - + ############################## <第 2 步,获取正文主字体> ################################## fsize_statiscs = {} for span in meta_span: @@ -481,32 +494,33 @@ def read_and_clean_pdf_text(fp): mega_sec = [] sec = [] for index, line in enumerate(meta_line): - if index == 0: + if index == 0: sec.append(line[fc]) continue if REMOVE_FOOT_NOTE: if meta_line[index][fs] <= give_up_fize_threshold: continue - if ffsize_same(meta_line[index][fs], meta_line[index-1][fs]): + if ffsize_same(meta_line[index][fs], meta_line[index - 1][fs]): # 尝试识别段落 - if meta_line[index][fc].endswith('.') and\ - (meta_line[index-1][fc] != 'NEW_BLOCK') and \ - (meta_line[index][fb][2] - meta_line[index][fb][0]) < (meta_line[index-1][fb][2] - meta_line[index-1][fb][0]) * 0.7: + if meta_line[index][fc].endswith('.') and \ + (meta_line[index - 1][fc] != 'NEW_BLOCK') and \ + (meta_line[index][fb][2] - meta_line[index][fb][0]) < ( + meta_line[index - 1][fb][2] - meta_line[index - 1][fb][0]) * 0.7: sec[-1] += line[fc] sec[-1] += "\n\n" else: sec[-1] += " " sec[-1] += line[fc] else: - if (index+1 < len(meta_line)) and \ - meta_line[index][fs] > main_fsize: + if (index + 1 < len(meta_line)) and \ + meta_line[index][fs] > main_fsize: # 单行 + 字体大 mega_sec.append(copy.deepcopy(sec)) sec = [] sec.append("# " + line[fc]) else: # 尝试识别section - if meta_line[index-1][fs] > meta_line[index][fs]: + if meta_line[index - 1][fs] > meta_line[index][fs]: sec.append("\n" + line[fc]) else: sec.append(line[fc]) @@ -525,13 +539,15 @@ def read_and_clean_pdf_text(fp): if len(block_txt) < 100: meta_txt[index] = '\n' return meta_txt + meta_txt = 把字符太少的块清除为回车(meta_txt) def 清理多余的空行(meta_txt): for index in reversed(range(1, len(meta_txt))): - if meta_txt[index] == '\n' and meta_txt[index-1] == '\n': + if meta_txt[index] == '\n' and meta_txt[index - 1] == '\n': meta_txt.pop(index) return meta_txt + meta_txt = 清理多余的空行(meta_txt) def 合并小写开头的段落块(meta_txt): @@ -542,16 +558,18 @@ def read_and_clean_pdf_text(fp): return True else: return False + for _ in range(100): for index, block_txt in enumerate(meta_txt): if starts_with_lowercase_word(block_txt): - if meta_txt[index-1] != '\n': - meta_txt[index-1] += ' ' + if meta_txt[index - 1] != '\n': + meta_txt[index - 1] += ' ' else: - meta_txt[index-1] = '' - meta_txt[index-1] += meta_txt[index] + meta_txt[index - 1] = '' + meta_txt[index - 1] += meta_txt[index] meta_txt[index] = '\n' return meta_txt + meta_txt = 合并小写开头的段落块(meta_txt) meta_txt = 清理多余的空行(meta_txt) @@ -571,7 +589,7 @@ def read_and_clean_pdf_text(fp): return meta_txt, page_one_meta -def get_files_from_everything(txt, type): # type='.md' +def get_files_from_everything(txt, type): # type='.md' """ 这个函数是用来获取指定目录下所有指定类型(如.md)的文件,并且对于网络上的文件,也可以获取它。 下面是对每个参数和返回值的说明: @@ -593,9 +611,10 @@ def get_files_from_everything(txt, type): # type='.md' from toolbox import get_conf proxies, = get_conf('proxies') r = requests.get(txt, proxies=proxies) - with open('./gpt_log/temp'+type, 'wb+') as f: f.write(r.content) + with open('./gpt_log/temp' + type, 'wb+') as f: + f.write(r.content) project_folder = './gpt_log/' - file_manifest = ['./gpt_log/temp'+type] + file_manifest = ['./gpt_log/temp' + type] elif txt.endswith(type): # 直接给定文件 file_manifest = [txt] @@ -603,7 +622,7 @@ def get_files_from_everything(txt, type): # type='.md' elif os.path.exists(txt): # 本地路径,递归搜索 project_folder = txt - file_manifest = [f for f in glob.glob(f'{project_folder}/**/*'+type, recursive=True)] + file_manifest = [f for f in glob.glob(f'{project_folder}/**/*' + type, recursive=True)] if len(file_manifest) == 0: success = False else: @@ -614,16 +633,14 @@ def get_files_from_everything(txt, type): # type='.md' return success, file_manifest, project_folder - - def Singleton(cls): _instance = {} - + def _singleton(*args, **kargs): if cls not in _instance: _instance[cls] = cls(*args, **kargs) return _instance[cls] - + return _singleton @@ -642,31 +659,30 @@ class knowledge_archive_interface(): from toolbox import ProxyNetworkActivate print('Checking Text2vec ...') from langchain.embeddings.huggingface import HuggingFaceEmbeddings - with ProxyNetworkActivate(): # 临时地激活代理网络 + with ProxyNetworkActivate(): # 临时地激活代理网络 self.text2vec_large_chinese = HuggingFaceEmbeddings(model_name="GanymedeNil/text2vec-large-chinese") return self.text2vec_large_chinese - def feed_archive(self, file_manifest, id="default"): self.threadLock.acquire() # import uuid self.current_id = id from zh_langchain import construct_vector_store - self.qa_handle, self.kai_path = construct_vector_store( - vs_id=self.current_id, - files=file_manifest, + self.qa_handle, self.kai_path = construct_vector_store( + vs_id=self.current_id, + files=file_manifest, sentence_size=100, history=[], one_conent="", one_content_segmentation="", - text2vec = self.get_chinese_text2vec(), + text2vec=self.get_chinese_text2vec(), ) self.threadLock.release() def get_current_archive_id(self): return self.current_id - + def get_loaded_file(self): return self.qa_handle.get_loaded_file() @@ -675,30 +691,31 @@ class knowledge_archive_interface(): if not self.current_id == id: self.current_id = id from zh_langchain import construct_vector_store - self.qa_handle, self.kai_path = construct_vector_store( - vs_id=self.current_id, - files=[], + self.qa_handle, self.kai_path = construct_vector_store( + vs_id=self.current_id, + files=[], sentence_size=100, history=[], one_conent="", one_content_segmentation="", - text2vec = self.get_chinese_text2vec(), + text2vec=self.get_chinese_text2vec(), ) VECTOR_SEARCH_SCORE_THRESHOLD = 0 VECTOR_SEARCH_TOP_K = 4 CHUNK_SIZE = 512 resp, prompt = self.qa_handle.get_knowledge_based_conent_test( - query = txt, - vs_path = self.kai_path, + query=txt, + vs_path=self.kai_path, score_threshold=VECTOR_SEARCH_SCORE_THRESHOLD, - vector_search_top_k=VECTOR_SEARCH_TOP_K, + vector_search_top_k=VECTOR_SEARCH_TOP_K, chunk_conent=True, chunk_size=CHUNK_SIZE, - text2vec = self.get_chinese_text2vec(), + text2vec=self.get_chinese_text2vec(), ) self.threadLock.release() return resp, prompt + def try_install_deps(deps): for dep in deps: import subprocess, sys diff --git a/requirements.txt b/requirements.txt index 690718c..41129fc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,4 +16,5 @@ openai numpy arxiv rich -langchain \ No newline at end of file +langchain +zh_langchain \ No newline at end of file From 344579fa795837e8a7d4b63e3643adb45ba2e0d5 Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 10:31:28 +0800 Subject: [PATCH 13/15] Every time the function is called, if the list parameter is not explicitly passed, the same default list will be used. This leads to the sharing of the same list object between function calls, resulting in a cumulative effect. --- request_llm/bridge_chatgpt.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/request_llm/bridge_chatgpt.py b/request_llm/bridge_chatgpt.py index eef8fbf..f20ceb6 100644 --- a/request_llm/bridge_chatgpt.py +++ b/request_llm/bridge_chatgpt.py @@ -28,6 +28,7 @@ proxies, API_KEY, TIMEOUT_SECONDS, MAX_RETRY = \ timeout_bot_msg = '[Local Message] Request timeout. Network error. Please check proxy settings in config.py.' + \ '网络错误,检查代理服务器是否可用,以及代理设置的格式是否正确,格式须是[协议]://[地址]:[端口],缺一不可。' + def get_full_error(chunk, stream_response): """ 获取完整的从Openai返回的报错 @@ -40,7 +41,7 @@ def get_full_error(chunk, stream_response): return chunk -def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="", observe_window=None, console_slience=False): +def predict_no_ui_long_connection(inputs, llm_kwargs, history=None, sys_prompt="", observe_window=None, console_slience=False): """ 发送至chatGPT,等待回复,一次性完成,不显示中间过程。但内部用stream的方法避免中途网线被掐。 inputs: @@ -54,7 +55,9 @@ def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="", observe_window = None: 用于负责跨越线程传递已经输出的部分,大部分时候仅仅为了fancy的视觉效果,留空即可。observe_window[0]:观测窗。observe_window[1]:看门狗 """ - watch_dog_patience = 5 # 看门狗的耐心, 设置5秒即可 + if history is None: + history = [] + watch_dog_patience = 5 # 看门狗的耐心, 设置5秒即可 headers, payload = generate_payload(inputs, llm_kwargs, history, system_prompt=sys_prompt, stream=True) retry = 0 while True: @@ -63,14 +66,14 @@ def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="", from .bridge_all import model_info endpoint = model_info[llm_kwargs['llm_model']]['endpoint'] response = requests.post(endpoint, headers=headers, proxies=proxies, - json=payload, stream=True, timeout=TIMEOUT_SECONDS); break + json=payload, stream=True, timeout=TIMEOUT_SECONDS); break except requests.exceptions.ReadTimeout as e: retry += 1 traceback.print_exc() if retry > MAX_RETRY: raise TimeoutError if MAX_RETRY!=0: print(f'请求超时,正在重试 ({retry}/{MAX_RETRY}) ……') - stream_response = response.iter_lines() + stream_response = response.iter_lines() result = '' while True: try: chunk = next(stream_response).decode() @@ -100,7 +103,8 @@ def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="", if len(observe_window) >= 2: if (time.time()-observe_window[1]) > watch_dog_patience: raise RuntimeError("用户取消了程序。") - else: raise RuntimeError("意外Json结构:"+delta) + else: + raise RuntimeError("意外Json结构:"+delta) if json_data['finish_reason'] == 'length': raise ConnectionAbortedError("正常结束,但显示Token不足,导致输出不完整,请削减单次输入的文本量。") return result From 0e1de5a1840faada39ed8de65ac3ec70e2d59d4f Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 10:57:52 +0800 Subject: [PATCH 14/15] Optimize the code to make it more readable, catch other exceptions, and avoid response contentless reading exceptions --- request_llm/bridge_chatgpt.py | 56 ++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/request_llm/bridge_chatgpt.py b/request_llm/bridge_chatgpt.py index f20ceb6..0a98b58 100644 --- a/request_llm/bridge_chatgpt.py +++ b/request_llm/bridge_chatgpt.py @@ -41,7 +41,9 @@ def get_full_error(chunk, stream_response): return chunk -def predict_no_ui_long_connection(inputs, llm_kwargs, history=None, sys_prompt="", observe_window=None, console_slience=False): +def predict_no_ui_long_connection( + inputs, llm_kwargs, history=None, sys_prompt="", observe_window=None, console_slience=False +): """ 发送至chatGPT,等待回复,一次性完成,不显示中间过程。但内部用stream的方法避免中途网线被掐。 inputs: @@ -63,25 +65,33 @@ def predict_no_ui_long_connection(inputs, llm_kwargs, history=None, sys_prompt=" while True: try: # make a POST request to the API endpoint, stream=False - from .bridge_all import model_info + from bridge_all import model_info endpoint = model_info[llm_kwargs['llm_model']]['endpoint'] response = requests.post(endpoint, headers=headers, proxies=proxies, - json=payload, stream=True, timeout=TIMEOUT_SECONDS); break - except requests.exceptions.ReadTimeout as e: + json=payload, stream=True, timeout=TIMEOUT_SECONDS) + break + except requests.exceptions.ReadTimeout: retry += 1 traceback.print_exc() - if retry > MAX_RETRY: raise TimeoutError - if MAX_RETRY!=0: print(f'请求超时,正在重试 ({retry}/{MAX_RETRY}) ……') + if retry > MAX_RETRY: + raise TimeoutError + if MAX_RETRY != 0: + print(f'请求超时,正在重试 ({retry}/{MAX_RETRY}) ……') + except Exception as e: + print(f"出现异常:{e}") + raise e stream_response = response.iter_lines() result = '' while True: - try: chunk = next(stream_response).decode() + try: + chunk = next(stream_response).decode() except StopIteration: break except requests.exceptions.ConnectionError: - chunk = next(stream_response).decode() # 失败了,重试一次?再失败就没办法了。 - if len(chunk)==0: continue + chunk = next(stream_response).decode() # 失败了,重试一次?再失败就没办法了。 + if len(chunk) == 0: + continue if not chunk.startswith('data:'): error_msg = get_full_error(chunk.encode('utf8'), stream_response).decode() if "reduce the length" in error_msg: @@ -95,7 +105,8 @@ def predict_no_ui_long_connection(inputs, llm_kwargs, history=None, sys_prompt=" if "role" in delta: continue if "content" in delta: result += delta["content"] - if not console_slience: print(delta["content"], end='') + if not console_slience: + print(delta["content"], end='') if observe_window is not None: # 观测窗,把已经获取的数据显示出去 if len(observe_window) >= 1: observe_window[0] += delta["content"] @@ -232,6 +243,7 @@ def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_promp yield from update_ui(chatbot=chatbot, history=history, msg="Json异常" + error_msg) # 刷新界面 return + def generate_payload(inputs, llm_kwargs, history, system_prompt, stream): """ 整合所有信息,选择LLM模型,生成http请求,为发送请求做准备 @@ -251,23 +263,19 @@ def generate_payload(inputs, llm_kwargs, history, system_prompt, stream): messages = [{"role": "system", "content": system_prompt}] if conversation_cnt: for index in range(0, 2*conversation_cnt, 2): - what_i_have_asked = {} - what_i_have_asked["role"] = "user" - what_i_have_asked["content"] = history[index] - what_gpt_answer = {} - what_gpt_answer["role"] = "assistant" - what_gpt_answer["content"] = history[index+1] + what_i_have_asked = {"role": "user", "content": history[index]} + what_gpt_answer = {"role": "assistant", "content": history[index + 1]} if what_i_have_asked["content"] != "": - if what_gpt_answer["content"] == "": continue - if what_gpt_answer["content"] == timeout_bot_msg: continue + if what_gpt_answer["content"] == "": + continue + if what_gpt_answer["content"] == timeout_bot_msg: + continue messages.append(what_i_have_asked) messages.append(what_gpt_answer) else: messages[-1]['content'] = what_gpt_answer['content'] - what_i_ask_now = {} - what_i_ask_now["role"] = "user" - what_i_ask_now["content"] = inputs + what_i_ask_now = {"role": "user", "content": inputs} messages.append(what_i_ask_now) payload = { @@ -282,8 +290,8 @@ def generate_payload(inputs, llm_kwargs, history, system_prompt, stream): } try: print(f" {llm_kwargs['llm_model']} : {conversation_cnt} : {inputs[:100]} ..........") - except: - print('输入中可能存在乱码。') - return headers,payload + except Exception as e: + print(f'输入中可能存在乱码。抛出异常: {e}') + return headers, payload From 2da36c766720c6b87eb929bc07e8f2da0f02c09d Mon Sep 17 00:00:00 2001 From: kainstan Date: Tue, 6 Jun 2023 16:10:54 +0800 Subject: [PATCH 15/15] Optimizing the code, requests. exceptions. ConnectionError should be written in the post request instead of reading from the iterator. If the post request is unsuccessful, it will not be executed to the iterator step. --- request_llm/bridge_chatgpt.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/request_llm/bridge_chatgpt.py b/request_llm/bridge_chatgpt.py index 0a98b58..5db3e8f 100644 --- a/request_llm/bridge_chatgpt.py +++ b/request_llm/bridge_chatgpt.py @@ -62,15 +62,16 @@ def predict_no_ui_long_connection( watch_dog_patience = 5 # 看门狗的耐心, 设置5秒即可 headers, payload = generate_payload(inputs, llm_kwargs, history, system_prompt=sys_prompt, stream=True) retry = 0 + from bridge_all import model_info while True: try: # make a POST request to the API endpoint, stream=False - from bridge_all import model_info endpoint = model_info[llm_kwargs['llm_model']]['endpoint'] response = requests.post(endpoint, headers=headers, proxies=proxies, json=payload, stream=True, timeout=TIMEOUT_SECONDS) + stream_response = response.iter_lines() break - except requests.exceptions.ReadTimeout: + except (requests.exceptions.ReadTimeout, requests.exceptions.ConnectionError): retry += 1 traceback.print_exc() if retry > MAX_RETRY: @@ -81,15 +82,14 @@ def predict_no_ui_long_connection( print(f"出现异常:{e}") raise e - stream_response = response.iter_lines() result = '' while True: try: chunk = next(stream_response).decode() except StopIteration: break - except requests.exceptions.ConnectionError: - chunk = next(stream_response).decode() # 失败了,重试一次?再失败就没办法了。 + # except requests.exceptions.ConnectionError: + # chunk = next(stream_response).decode() # 失败了,重试一次?再失败就没办法了。 if len(chunk) == 0: continue if not chunk.startswith('data:'): @@ -98,11 +98,14 @@ def predict_no_ui_long_connection( raise ConnectionAbortedError("OpenAI拒绝了请求:" + error_msg) else: raise RuntimeError("OpenAI拒绝了请求:" + error_msg) - if ('data: [DONE]' in chunk): break # api2d 正常完成 + if 'data: [DONE]' in chunk: + break # api2d 正常完成 json_data = json.loads(chunk.lstrip('data:'))['choices'][0] delta = json_data["delta"] - if len(delta) == 0: break - if "role" in delta: continue + if len(delta) == 0: + break + if "role" in delta: + continue if "content" in delta: result += delta["content"] if not console_slience: