diff --git a/.gitignore b/.gitignore index efe469e..987f054 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,6 @@ coverage.xml *.pot github .github -.idea/ TEMP TRASH @@ -144,3 +143,5 @@ private_upload other_llms cradle* debug* +private* +crazy_functions/test_project/pdf_and_word diff --git a/Dockerfile b/Dockerfile index 757a188..da5053d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,6 @@ +# 此Dockerfile适用于“无本地模型”的环境构建,如果需要使用chatglm等本地模型,请参考 docs/Dockerfile+ChatGLM +# 如何构建: 先修改 `config.py`, 然后 docker build -t gpt-academic . +# 如何运行: docker run --rm -it --net=host gpt-academic FROM python:3.11 RUN echo '[global]' > /etc/pip.conf && \ @@ -11,4 +14,7 @@ RUN pip3 install -r requirements.txt COPY . . +# 可选步骤,用于预热模块 +RUN python3 -c 'from check_proxy import warm_up_modules; warm_up_modules()' + CMD ["python3", "-u", "main.py"] diff --git a/README.md b/README.md index 96b0435..a97026e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# ChatGPT 学术优化 + + +# ChatGPT 学术优化 **如果喜欢这个项目,请给它一个Star;如果你发明了更好用的快捷键或函数插件,欢迎发issue或者pull requests** @@ -21,7 +23,7 @@ If you like this project, please give it a Star. If you've come up with more use 一键代码解释 | 可以正确显示代码、解释代码 [自定义快捷键](https://www.bilibili.com/video/BV14s4y1E7jN) | 支持自定义快捷键 [配置代理服务器](https://www.bilibili.com/video/BV1rc411W7Dr) | 支持配置代理服务器 -模块化设计 | 支持自定义高阶的实验性功能与[函数插件],插件支持[热更新](https://github.com/binary-husky/chatgpt_academic/wiki/%E5%87%BD%E6%95%B0%E6%8F%92%E4%BB%B6%E6%8C%87%E5%8D%97) +模块化设计 | 支持自定义高阶的函数插件与[函数插件],插件支持[热更新](https://github.com/binary-husky/chatgpt_academic/wiki/%E5%87%BD%E6%95%B0%E6%8F%92%E4%BB%B6%E6%8C%87%E5%8D%97) [自我程序剖析](https://www.bilibili.com/video/BV1cj411A7VW) | [函数插件] [一键读懂](https://github.com/binary-husky/chatgpt_academic/wiki/chatgpt-academic%E9%A1%B9%E7%9B%AE%E8%87%AA%E8%AF%91%E8%A7%A3%E6%8A%A5%E5%91%8A)本项目的源代码 [程序剖析](https://www.bilibili.com/video/BV1cj411A7VW) | [函数插件] 一键可以剖析其他Python/C/C++/Java/Lua/...项目树 读论文 | [函数插件] 一键解读latex论文全文并生成摘要 @@ -76,16 +78,17 @@ huggingface免科学上网[在线体验](https://huggingface.co/spaces/qingxu98/ v3.1的[huggingface测试版](https://huggingface.co/spaces/qingxu98/academic-chatgpt-beta)(huggingface版不支持chatglm) +--- -## 直接运行 (Windows, Linux or MacOS) +## 安装-方法1:直接运行 (Windows, Linux or MacOS) -### 1. 下载项目 +1. 下载项目 ```sh git clone https://github.com/binary-husky/chatgpt_academic.git cd chatgpt_academic ``` -### 2. 配置API_KEY和代理设置 +2. 配置API_KEY和代理设置 在`config.py`中,配置 海外Proxy 和 OpenAI API KEY,说明如下 ``` @@ -96,7 +99,7 @@ cd chatgpt_academic (P.S. 程序运行时会优先检查是否存在名为`config_private.py`的私密配置文件,并用其中的配置覆盖`config.py`的同名配置。因此,如果您能理解我们的配置读取逻辑,我们强烈建议您在`config.py`旁边创建一个名为`config_private.py`的新配置文件,并把`config.py`中的配置转移(复制)到`config_private.py`中。`config_private.py`不受git管控,可以让您的隐私信息更加安全。) -### 3. 安装依赖 +3. 安装依赖 ```sh # (选择一)推荐 python -m pip install -r requirements.txt @@ -110,27 +113,30 @@ python -m pip install -r requirements.txt # python -m pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ ``` -### 4. 运行 +如果需要支持清华ChatGLM,需要额外安装更多依赖(不熟悉python者、电脑配置不佳者,建议不要尝试): +```sh +python -m pip install -r request_llm/requirements_chatglm.txt +``` + +4. 运行 ```sh python main.py ``` -### 5. 测试实验性功能 +5. 测试函数插件 ``` -- 测试C++项目头文件分析 - input区域 输入 `./crazy_functions/test_project/cpp/libJPG` , 然后点击 "[实验] 解析整个C++项目(input输入项目根路径)" -- 测试给Latex项目写摘要 - input区域 输入 `./crazy_functions/test_project/latex/attention` , 然后点击 "[实验] 读tex论文写摘要(input输入项目根路径)" - 测试Python项目分析 - input区域 输入 `./crazy_functions/test_project/python/dqn` , 然后点击 "[实验] 解析整个py项目(input输入项目根路径)" + input区域 输入 `./crazy_functions/test_project/python/dqn` , 然后点击 "解析整个Python项目" - 测试自我代码解读 - 点击 "[实验] 请解析并解构此项目本身" + 点击 "[多线程Demo] 解析此项目本身(源码自译解)" - 测试实验功能模板函数(要求gpt回答历史上的今天发生了什么),您可以根据此函数为模板,实现更复杂的功能 - 点击 "[实验] 实验功能函数模板" + 点击 "[函数插件模板Demo] 历史上的今天" +- 函数插件区下拉菜单中有更多功能可供选择 ``` -## 使用docker (Linux) +## 安装-方法2:使用docker (Linux) +1. 仅ChatGPT(推荐大多数人选择) ``` sh # 下载项目 git clone https://github.com/binary-husky/chatgpt_academic.git @@ -142,67 +148,70 @@ docker build -t gpt-academic . # 运行 docker run --rm -it --net=host gpt-academic -# 测试实验性功能 -## 测试自我代码解读 -点击 "[实验] 请解析并解构此项目本身" -## 测试实验功能模板函数(要求gpt回答历史上的今天发生了什么),您可以根据此函数为模板,实现更复杂的功能 -点击 "[实验] 实验功能函数模板" -##(请注意在docker中运行时,需要额外注意程序的文件访问权限问题) -## 测试C++项目头文件分析 -input区域 输入 ./crazy_functions/test_project/cpp/libJPG , 然后点击 "[实验] 解析整个C++项目(input输入项目根路径)" +# 测试函数插件 +## 测试函数插件模板函数(要求gpt回答历史上的今天发生了什么),您可以根据此函数为模板,实现更复杂的功能 +点击 "[函数插件模板Demo] 历史上的今天" ## 测试给Latex项目写摘要 -input区域 输入 ./crazy_functions/test_project/latex/attention , 然后点击 "[实验] 读tex论文写摘要(input输入项目根路径)" +input区域 输入 ./crazy_functions/test_project/latex/attention , 然后点击 "读Tex论文写摘要" ## 测试Python项目分析 -input区域 输入 ./crazy_functions/test_project/python/dqn , 然后点击 "[实验] 解析整个py项目(input输入项目根路径)" +input区域 输入 ./crazy_functions/test_project/python/dqn , 然后点击 "解析整个Python项目" +函数插件区下拉菜单中有更多功能可供选择 ``` -## 其他部署方式 +2. ChatGPT+ChatGLM(需要对docker非常熟悉 + 电脑配置足够强) -- 远程云服务器部署 -请访问[部署wiki-2](https://github.com/binary-husky/chatgpt_academic/wiki/%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%9C%E7%A8%8B%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97) +``` sh +# 修改dockerfile +cd docs && nano Dockerfile+ChatGLM +# How to build | 如何构建 (Dockerfile+ChatGLM在docs路径下,请先cd docs) +docker build -t gpt-academic --network=host -f Dockerfile+ChatGLM . +# How to run | 如何运行 (1) 直接运行: +docker run --rm -it --net=host --gpus=all gpt-academic +# How to run | 如何运行 (2) 我想运行之前进容器做一些调整: +docker run --rm -it --net=host --gpus=all gpt-academic bash +``` -- 使用WSL2(Windows Subsystem for Linux 子系统) -请访问[部署wiki-1](https://github.com/binary-husky/chatgpt_academic/wiki/%E4%BD%BF%E7%94%A8WSL2%EF%BC%88Windows-Subsystem-for-Linux-%E5%AD%90%E7%B3%BB%E7%BB%9F%EF%BC%89%E9%83%A8%E7%BD%B2) + +## 安装-方法3:其他部署方式 + +1. 远程云服务器部署 +请访问[部署wiki-1](https://github.com/binary-husky/chatgpt_academic/wiki/%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%9C%E7%A8%8B%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97) + +2. 使用WSL2(Windows Subsystem for Linux 子系统) +请访问[部署wiki-2](https://github.com/binary-husky/chatgpt_academic/wiki/%E4%BD%BF%E7%94%A8WSL2%EF%BC%88Windows-Subsystem-for-Linux-%E5%AD%90%E7%B3%BB%E7%BB%9F%EF%BC%89%E9%83%A8%E7%BD%B2) + + +## 安装-代理配置 +### 方法一:常规方法 +[配置代理](https://github.com/binary-husky/chatgpt_academic/issues/1) + +### 方法二:纯新手教程 +[纯新手教程](https://github.com/binary-husky/chatgpt_academic/wiki/%E4%BB%A3%E7%90%86%E8%BD%AF%E4%BB%B6%E9%97%AE%E9%A2%98%E7%9A%84%E6%96%B0%E6%89%8B%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95%EF%BC%88%E6%96%B9%E6%B3%95%E5%8F%AA%E9%80%82%E7%94%A8%E4%BA%8E%E6%96%B0%E6%89%8B%EF%BC%89) + + +--- ## 自定义新的便捷按钮(学术快捷键自定义) 任意文本编辑器打开`core_functional.py`,添加条目如下,然后重启程序即可。(如果按钮已经添加成功并可见,那么前缀、后缀都支持热修改,无需重启程序即可生效。) 例如 ``` "超级英译中": { - # 前缀,会被加在你的输入之前。例如,用来描述你的要求,例如翻译、解释代码、润色等等 "Prefix": "请翻译把下面一段内容成中文,然后用一个markdown表格逐一解释文中出现的专有名词:\n\n", # 后缀,会被加在你的输入之后。例如,配合前缀可以把你的输入内容用引号圈起来。 "Suffix": "", - }, ```
+--- -如果你发明了更好用的快捷键,欢迎发issue或者pull requests! -## 配置代理 -### 方法一:常规方法 -在```config.py```中修改端口与代理软件对应 - -
- - -
- -配置完成后,你可以用以下命令测试代理是否工作,如果一切正常,下面的代码将输出你的代理服务器所在地: -``` -python check_proxy.py -``` -### 方法二:纯新手教程 -[纯新手教程](https://github.com/binary-husky/chatgpt_academic/wiki/%E4%BB%A3%E7%90%86%E8%BD%AF%E4%BB%B6%E9%97%AE%E9%A2%98%E7%9A%84%E6%96%B0%E6%89%8B%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95%EF%BC%88%E6%96%B9%E6%B3%95%E5%8F%AA%E9%80%82%E7%94%A8%E4%BA%8E%E6%96%B0%E6%89%8B%EF%BC%89) - -## 功能测试 +## 部分功能展示 ### 图片显示: @@ -256,8 +265,9 @@ python check_proxy.py ## Todo 与 版本规划: - -- version 3.0 (Todo): 优化对chatglm和其他小型llm的支持 +- version 3.2+ (todo): 函数插件支持更多参数接口 +- version 3.1: 支持同时问询多个gpt模型!支持api2d,支持多个apikey负载均衡 +- version 3.0: 对chatglm和其他小型llm的支持 - version 2.6: 重构了插件结构,提高了交互性,加入更多插件 - version 2.5: 自更新,解决总结大工程源代码时文本过长、token溢出的问题 - version 2.4: (1)新增PDF全文翻译功能; (2)新增输入区切换位置的功能; (3)新增垂直布局选项; (4)多线程函数插件优化。 @@ -269,14 +279,12 @@ python check_proxy.py ## 参考与学习 - ``` 代码中参考了很多其他优秀项目中的设计,主要包括: -# 借鉴项目1:借鉴了ChuanhuChatGPT中读取OpenAI json的方法、记录历史问询记录的方法以及gradio queue的使用技巧 +# 借鉴项目1:借鉴了ChuanhuChatGPT中诸多技巧 https://github.com/GaiZhenbiao/ChuanhuChatGPT -# 借鉴项目2: +# 借鉴项目2:清华ChatGLM-6B: https://github.com/THUDM/ChatGLM-6B - ``` diff --git a/check_proxy.py b/check_proxy.py index 7fdd2b0..28711a8 100644 --- a/check_proxy.py +++ b/check_proxy.py @@ -103,7 +103,7 @@ def auto_update(): import json proxies, = get_conf('proxies') response = requests.get( - "https://raw.githubusercontent.com/binary-husky/chatgpt_academic/master/version", proxies=proxies, timeout=1) + "https://raw.githubusercontent.com/binary-husky/chatgpt_academic/master/version", proxies=proxies, timeout=5) remote_json_data = json.loads(response.text) remote_version = remote_json_data['version'] if remote_json_data["show_feature"]: @@ -133,6 +133,13 @@ def auto_update(): except: print('自动更新程序:已禁用') +def warm_up_modules(): + print('正在执行一些模块的预热...') + from request_llm.bridge_all import model_info + enc = model_info["gpt-3.5-turbo"]['tokenizer'] + enc.encode("模块预热", disallowed_special=()) + enc = model_info["gpt-4"]['tokenizer'] + enc.encode("模块预热", disallowed_special=()) if __name__ == '__main__': import os diff --git a/config.py b/config.py index 658de99..8c01531 100644 --- a/config.py +++ b/config.py @@ -1,5 +1,5 @@ # [step 1]>> 例如: API_KEY = "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" (此key无效) -API_KEY = "sk-此处填API密钥" +API_KEY = "sk-此处填API密钥" # 可同时填写多个API-KEY,用英文逗号分割,例如API_KEY = "sk-openaikey1,sk-openaikey2,fkxxxx-api2dkey1,fkxxxx-api2dkey2" # [step 2]>> 改为True应用代理,如果直接在海外服务器部署,此处不修改 USE_PROXY = False @@ -19,13 +19,12 @@ if USE_PROXY: else: proxies = None -# 多线程函数插件中,默认允许多少路线程同时访问OpenAI。 -# Free trial users的限制是每分钟3次,Pay-as-you-go users的限制是每分钟3500次。提高限制请查询: -# https://platform.openai.com/docs/guides/rate-limits/overview +# [step 3]>> 多线程函数插件中,默认允许多少路线程同时访问OpenAI。Free trial users的限制是每分钟3次,Pay-as-you-go users的限制是每分钟3500次 +# 一言以蔽之:免费用户填3,OpenAI绑了信用卡的用户可以填 16 或者更高。提高限制请查询:https://platform.openai.com/docs/guides/rate-limits/overview DEFAULT_WORKER_NUM = 3 -# [step 3]>> 以下配置可以优化体验,但大部分场合下并不需要修改 +# [step 4]>> 以下配置可以优化体验,但大部分场合下并不需要修改 # 对话窗的高度 CHATBOT_HEIGHT = 1115 @@ -45,14 +44,15 @@ WEB_PORT = -1 MAX_RETRY = 2 # OpenAI模型选择是(gpt4现在只对申请成功的人开放) -LLM_MODEL = "gpt-3.5-turbo" +LLM_MODEL = "gpt-3.5-turbo" # 可选 ↓↓↓ +AVAIL_LLM_MODELS = ["gpt-3.5-turbo", "api2d-gpt-3.5-turbo", "gpt-4", "api2d-gpt-4", "chatglm"] -# OpenAI的API_URL -API_URL = "https://api.openai.com/v1/chat/completions" +# 本地LLM模型如ChatGLM的执行方式 CPU/GPU +LOCAL_MODEL_DEVICE = "cpu" # 可选 "cuda" -# 设置并行使用的线程数 +# 设置gradio的并行线程数(不需要修改) CONCURRENT_COUNT = 100 -# 设置用户名和密码(相关功能不稳定,与gradio版本和网络都相关,如果本地使用不建议加这个) +# 设置用户名和密码(不需要修改)(相关功能不稳定,与gradio版本和网络都相关,如果本地使用不建议加这个) # [("username", "password"), ("username2", "password2"), ...] AUTHENTICATION = [] diff --git a/crazy_functional.py b/crazy_functional.py index af308b7..6f4d37e 100644 --- a/crazy_functional.py +++ b/crazy_functional.py @@ -3,7 +3,6 @@ from toolbox import HotReload # HotReload 的意思是热更新,修改函数 def get_crazy_functions(): ###################### 第一组插件 ########################### - # [第一组插件]: 最早期编写的项目插件和一些demo from crazy_functions.读文章写摘要 import 读文章写摘要 from crazy_functions.生成函数注释 import 批量生成函数注释 from crazy_functions.解析项目源代码 import 解析项目本身 @@ -16,16 +15,23 @@ def get_crazy_functions(): from crazy_functions.高级功能函数模板 import 高阶功能模板函数 from crazy_functions.代码重写为全英文_多线程 import 全项目切换英文 from crazy_functions.Latex全文润色 import Latex英文润色 + from crazy_functions.询问多个大语言模型 import 同时问询 from crazy_functions.解析项目源代码 import 解析一个Lua项目 from crazy_functions.解析项目源代码 import 解析一个CSharp项目 + from crazy_functions.总结word文档 import 总结word文档 function_plugins = { "解析整个Python项目": { "Color": "stop", # 按钮颜色 "Function": HotReload(解析一个Python项目) }, + "批量总结Word文档": { + "Color": "stop", + "Function": HotReload(总结word文档) + }, "解析整个C++项目头文件": { "Color": "stop", # 按钮颜色 + "AsButton": False, # 加入下拉菜单中 "Function": HotReload(解析一个C项目的头文件) }, "解析整个C++项目(.cpp/.hpp/.c/.h)": { @@ -81,10 +87,9 @@ def get_crazy_functions(): } ###################### 第二组插件 ########################### - # [第二组插件]: 经过充分测试,但功能上距离达到完美状态还差一点点 + # [第二组插件]: 经过充分测试 from crazy_functions.批量总结PDF文档 import 批量总结PDF文档 from crazy_functions.批量总结PDF文档pdfminer import 批量总结PDF文档pdfminer - from crazy_functions.总结word文档 import 总结word文档 from crazy_functions.批量翻译PDF文档_多线程 import 批量翻译PDF文档 from crazy_functions.谷歌检索小助手 import 谷歌检索小助手 from crazy_functions.理解PDF文档内容 import 理解PDF文档内容标准文件输入 @@ -100,6 +105,10 @@ def get_crazy_functions(): "AsButton": True, # 加入下拉菜单中 "Function": HotReload(批量翻译PDF文档) }, + "询问多个GPT模型": { + "Color": "stop", # 按钮颜色 + "Function": HotReload(同时问询) + }, "[测试功能] 批量总结PDF文档": { "Color": "stop", "AsButton": False, # 加入下拉菜单中 @@ -116,10 +125,7 @@ def get_crazy_functions(): "AsButton": False, # 加入下拉菜单中 "Function": HotReload(谷歌检索小助手) }, - "批量总结Word文档": { - "Color": "stop", - "Function": HotReload(总结word文档) - }, + "理解PDF文档内容 (模仿ChatPDF)": { # HotReload 的意思是热更新,修改函数插件代码后,不需要重启程序,代码直接生效 "Color": "stop", diff --git a/crazy_functions/Latex全文润色.py b/crazy_functions/Latex全文润色.py index 46026fb..c299e59 100644 --- a/crazy_functions/Latex全文润色.py +++ b/crazy_functions/Latex全文润色.py @@ -11,9 +11,8 @@ class PaperFileGroup(): self.sp_file_tag = [] # count_token - import tiktoken - from toolbox import get_conf - enc = tiktoken.encoding_for_model(*get_conf('LLM_MODEL')) + 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=())) self.get_token_num = get_token_num diff --git a/crazy_functions/Latex全文翻译.py b/crazy_functions/Latex全文翻译.py index 06b4502..efada61 100644 --- a/crazy_functions/Latex全文翻译.py +++ b/crazy_functions/Latex全文翻译.py @@ -11,9 +11,8 @@ class PaperFileGroup(): self.sp_file_tag = [] # count_token - import tiktoken - from toolbox import get_conf - enc = tiktoken.encoding_for_model(*get_conf('LLM_MODEL')) + 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=())) self.get_token_num = get_token_num diff --git a/crazy_functions/crazy_functions_test.py b/crazy_functions/crazy_functions_test.py new file mode 100644 index 0000000..2838e54 --- /dev/null +++ b/crazy_functions/crazy_functions_test.py @@ -0,0 +1,92 @@ +""" +这是什么? + 这个文件用于函数插件的单元测试 + 运行方法 python crazy_functions/crazy_functions_test.py +""" + +def validate_path(): + import os, sys + dir_name = os.path.dirname(__file__) + root_dir_assume = os.path.abspath(os.path.dirname(__file__) + '/..') + os.chdir(root_dir_assume) + sys.path.append(root_dir_assume) + +validate_path() # validate path so you can run from base directory + +from toolbox import get_conf, ChatBotWithCookies +proxies, WEB_PORT, LLM_MODEL, CONCURRENT_COUNT, AUTHENTICATION, CHATBOT_HEIGHT, LAYOUT, API_KEY = \ + get_conf('proxies', 'WEB_PORT', 'LLM_MODEL', 'CONCURRENT_COUNT', 'AUTHENTICATION', 'CHATBOT_HEIGHT', 'LAYOUT', 'API_KEY') + +llm_kwargs = { + 'api_key': API_KEY, + 'llm_model': LLM_MODEL, + 'top_p':1.0, + 'max_length': None, + 'temperature':1.0, +} +plugin_kwargs = { } +chatbot = ChatBotWithCookies(llm_kwargs) +history = [] +system_prompt = "Serve me as a writing and programming assistant." +web_port = 1024 + + +def test_解析一个Python项目(): + from crazy_functions.解析项目源代码 import 解析一个Python项目 + txt = "crazy_functions/test_project/python/dqn" + for cookies, cb, hist, msg in 解析一个Python项目(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port): + print(cb) + +def test_解析一个Cpp项目(): + from crazy_functions.解析项目源代码 import 解析一个C项目 + txt = "crazy_functions/test_project/cpp/cppipc" + for cookies, cb, hist, msg in 解析一个C项目(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port): + print(cb) + +def test_Latex英文润色(): + from crazy_functions.Latex全文润色 import Latex英文润色 + txt = "crazy_functions/test_project/latex/attention" + for cookies, cb, hist, msg in Latex英文润色(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port): + print(cb) + +def test_Markdown中译英(): + from crazy_functions.批量Markdown翻译 import Markdown中译英 + txt = "README.md" + for cookies, cb, hist, msg in Markdown中译英(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port): + print(cb) + +def test_批量翻译PDF文档(): + from crazy_functions.批量翻译PDF文档_多线程 import 批量翻译PDF文档 + txt = "crazy_functions/test_project/pdf_and_word" + for cookies, cb, hist, msg in 批量翻译PDF文档(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port): + print(cb) + +def test_谷歌检索小助手(): + from crazy_functions.谷歌检索小助手 import 谷歌检索小助手 + txt = "https://scholar.google.com/scholar?hl=en&as_sdt=0%2C5&q=auto+reinforcement+learning&btnG=" + for cookies, cb, hist, msg in 谷歌检索小助手(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port): + print(cb) + +def test_总结word文档(): + from crazy_functions.总结word文档 import 总结word文档 + txt = "crazy_functions/test_project/pdf_and_word" + for cookies, cb, hist, msg in 总结word文档(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port): + print(cb) + +def test_下载arxiv论文并翻译摘要(): + from crazy_functions.下载arxiv论文翻译摘要 import 下载arxiv论文并翻译摘要 + txt = "1812.10695" + for cookies, cb, hist, msg in 下载arxiv论文并翻译摘要(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port): + print(cb) + +test_解析一个Python项目() +test_Latex英文润色() +test_Markdown中译英() +test_批量翻译PDF文档() +test_谷歌检索小助手() +test_总结word文档() +test_下载arxiv论文并翻译摘要() +test_解析一个Cpp项目() + +input("程序完成,回车退出。") +print("退出。") \ No newline at end of file diff --git a/crazy_functions/crazy_utils.py b/crazy_functions/crazy_utils.py index ba5f18c..4e0eba4 100644 --- a/crazy_functions/crazy_utils.py +++ b/crazy_functions/crazy_utils.py @@ -2,9 +2,9 @@ import traceback from toolbox import update_ui, get_conf def input_clipping(inputs, history, max_token_limit): - import tiktoken import numpy as np - enc = tiktoken.encoding_for_model(*get_conf('LLM_MODEL')) + 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=())) mode = 'input-and-history' @@ -61,12 +61,12 @@ def request_gpt_model_in_new_thread_with_ui_alive( """ import time from concurrent.futures import ThreadPoolExecutor - from request_llm.bridge_chatgpt import predict_no_ui_long_connection + from request_llm.bridge_all import predict_no_ui_long_connection # 用户反馈 chatbot.append([inputs_show_user, ""]) yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面 executor = ThreadPoolExecutor(max_workers=16) - mutable = ["", time.time()] + mutable = ["", time.time(), ""] def _req_gpt(inputs, history, sys_prompt): retry_op = retry_times_at_unknown_error exceeded_cnt = 0 @@ -105,7 +105,7 @@ def request_gpt_model_in_new_thread_with_ui_alive( 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" - if "Rate limit reached" in tb_str: + if ("Rate limit reached" in tb_str) or ("Too Many Requests" in tb_str): time.sleep(30) time.sleep(5) continue # 返回重试 @@ -167,13 +167,17 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency( """ import time, random from concurrent.futures import ThreadPoolExecutor - from request_llm.bridge_chatgpt import predict_no_ui_long_connection + 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 <= 0 or max_workers >= 20: max_workers = 8 + # 屏蔽掉 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) # 用户反馈 @@ -230,9 +234,9 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency( if retry_op > 0: retry_op -= 1 wait = random.randint(5, 20) - if "Rate limit reached" in tb_str: + if ("Rate limit reached" in tb_str) or ("Too Many Requests" in tb_str): wait = wait * 3 - fail_info = "OpenAI请求速率限制 " + fail_info = "OpenAI绑定信用卡可解除频率限制 " else: fail_info = "" # 也许等待十几秒后,情况会好转 diff --git a/crazy_functions/代码重写为全英文_多线程.py b/crazy_functions/代码重写为全英文_多线程.py index 40bc45d..e57f80f 100644 --- a/crazy_functions/代码重写为全英文_多线程.py +++ b/crazy_functions/代码重写为全英文_多线程.py @@ -1,5 +1,5 @@ import threading -from request_llm.bridge_chatgpt import predict_no_ui_long_connection +from request_llm.bridge_all import predict_no_ui_long_connection from toolbox import update_ui from toolbox import CatchException, write_results_to_file, report_execption from .crazy_utils import breakdown_txt_to_satisfy_token_limit @@ -59,9 +59,8 @@ def 全项目切换英文(txt, llm_kwargs, plugin_kwargs, chatbot, history, sys_ # 第5步:Token限制下的截断与处理 MAX_TOKEN = 3000 - import tiktoken - from toolbox import get_conf - enc = tiktoken.encoding_for_model(*get_conf('LLM_MODEL')) + from request_llm.bridge_all import model_info + enc = model_info["gpt-3.5-turbo"]['tokenizer'] def get_token_fn(txt): return len(enc.encode(txt, disallowed_special=())) diff --git a/crazy_functions/总结word文档.py b/crazy_functions/总结word文档.py index 742c7ab..f1fe201 100644 --- a/crazy_functions/总结word文档.py +++ b/crazy_functions/总结word文档.py @@ -8,8 +8,6 @@ def 解析docx(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot import time, os # pip install python-docx 用于docx格式,跨平台 # pip install pywin32 用于doc格式,仅支持Win平台 - - print('begin analysis on:', file_manifest) for index, fp in enumerate(file_manifest): if fp.split(".")[-1] == "docx": from docx import Document @@ -29,18 +27,20 @@ def 解析docx(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot word.Quit() print(file_content) - - prefix = "接下来请你逐文件分析下面的论文文件," if index == 0 else "" # private_upload里面的文件名在解压zip后容易出现乱码(rar和7z格式正常),故可以只分析文章内容,不输入文件名 - i_say = prefix + f'请对下面的文章片段用中英文做概述,文件名是{os.path.relpath(fp, project_folder)},' \ - f'文章内容是 ```{file_content}```' - i_say_show_user = prefix + f'[{index+1}/{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) # 刷新界面 - - if not fast_debug: - msg = '正常' - # ** gpt request ** + from .crazy_utils import breakdown_txt_to_satisfy_token_limit_for_pdf + from request_llm.bridge_all import model_info + max_token = model_info[llm_kwargs['llm_model']]['max_token'] + TOKEN_LIMIT_PER_FRAGMENT = max_token * 3 // 4 + paper_fragments = breakdown_txt_to_satisfy_token_limit_for_pdf( + txt=file_content, + get_token_fn=model_info[llm_kwargs['llm_model']]['token_cnt'], + limit=TOKEN_LIMIT_PER_FRAGMENT + ) + this_paper_history = [] + for i, paper_frag in enumerate(paper_fragments): + i_say = f'请对下面的文章片段用中文做概述,文件名是{os.path.relpath(fp, project_folder)},文章内容是 ```{paper_frag}```' + i_say_show_user = f'请对下面的文章片段做概述: {os.path.abspath(fp)}的第{i+1}/{len(paper_fragments)}个片段。' gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive( inputs=i_say, inputs_show_user=i_say_show_user, @@ -48,46 +48,34 @@ def 解析docx(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot chatbot=chatbot, history=[], sys_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) # 刷新界面 - if not fast_debug: time.sleep(2) + history.extend([i_say_show_user,gpt_say]) + this_paper_history.extend([i_say_show_user,gpt_say]) - """ - # 可按需启用 - i_say = f'根据你上述的分析,对全文进行概括,用学术性语言写一段中文摘要,然后再写一篇英文的。' - chatbot.append((i_say, "[Local Message] waiting gpt response.")) - yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 + # 已经对该文章的所有片段总结完毕,如果文章被切分了, + if len(paper_fragments) > 1: + i_say = f"根据以上的对话,总结文章{os.path.abspath(fp)}的主要内容。" + gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive( + inputs=i_say, + inputs_show_user=i_say, + llm_kwargs=llm_kwargs, + chatbot=chatbot, + history=this_paper_history, + sys_prompt="总结文章。" + ) + history.extend([i_say,gpt_say]) + this_paper_history.extend([i_say,gpt_say]) - i_say = f'我想让你做一个论文写作导师。您的任务是使用人工智能工具(例如自然语言处理)提供有关如何改进其上述文章的反馈。' \ - f'您还应该利用您在有效写作技巧方面的修辞知识和经验来建议作者可以更好地以书面形式表达他们的想法和想法的方法。' \ - f'根据你之前的分析,提出建议' - chatbot.append((i_say, "[Local Message] waiting gpt response.")) - yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 - - """ - - if not fast_debug: - msg = '正常' - # ** gpt request ** - gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive( - inputs=i_say, - inputs_show_user=i_say, - llm_kwargs=llm_kwargs, - chatbot=chatbot, - history=history, - sys_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) # 刷新界面 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) # 刷新界面 + + res = write_results_to_file(history) + chatbot.append(("所有文件都总结完成了吗?", res)) + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 @CatchException @@ -123,11 +111,11 @@ def 总结word文档(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_pr return # 搜索需要处理的文件清单 - file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.docx', recursive=True)] + \ - [f for f in glob.glob(f'{project_folder}/**/*.doc', recursive=True)] - # [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 txt.endswith('.docx') or txt.endswith('.doc'): + file_manifest = [txt] + else: + file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.docx', recursive=True)] + \ + [f for f in glob.glob(f'{project_folder}/**/*.doc', recursive=True)] # 如果没找到任何文件 if len(file_manifest) == 0: diff --git a/crazy_functions/批量Markdown翻译.py b/crazy_functions/批量Markdown翻译.py index 119d3a5..68d1b50 100644 --- a/crazy_functions/批量Markdown翻译.py +++ b/crazy_functions/批量Markdown翻译.py @@ -11,9 +11,8 @@ class PaperFileGroup(): self.sp_file_tag = [] # count_token - import tiktoken - from toolbox import get_conf - enc = tiktoken.encoding_for_model(*get_conf('LLM_MODEL')) + 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=())) self.get_token_num = get_token_num diff --git a/crazy_functions/批量翻译PDF文档_多线程.py b/crazy_functions/批量翻译PDF文档_多线程.py index 42e75b4..4adb9a4 100644 --- a/crazy_functions/批量翻译PDF文档_多线程.py +++ b/crazy_functions/批量翻译PDF文档_多线程.py @@ -68,8 +68,8 @@ def 解析PDF(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, # 递归地切割PDF文件 from .crazy_utils import breakdown_txt_to_satisfy_token_limit_for_pdf - from toolbox import get_conf - enc = tiktoken.encoding_for_model(*get_conf('LLM_MODEL')) + 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=())) paper_fragments = breakdown_txt_to_satisfy_token_limit_for_pdf( txt=file_content, get_token_fn=get_token_num, limit=TOKEN_LIMIT_PER_FRAGMENT) diff --git a/crazy_functions/理解PDF文档内容.py b/crazy_functions/理解PDF文档内容.py index 168b2c9..5050864 100644 --- a/crazy_functions/理解PDF文档内容.py +++ b/crazy_functions/理解PDF文档内容.py @@ -17,8 +17,8 @@ def 解析PDF(file_name, llm_kwargs, plugin_kwargs, chatbot, history, system_pro TOKEN_LIMIT_PER_FRAGMENT = 2500 from .crazy_utils import breakdown_txt_to_satisfy_token_limit_for_pdf - from toolbox import get_conf - enc = tiktoken.encoding_for_model(*get_conf('LLM_MODEL')) + 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=())) paper_fragments = breakdown_txt_to_satisfy_token_limit_for_pdf( txt=file_content, get_token_fn=get_token_num, limit=TOKEN_LIMIT_PER_FRAGMENT) diff --git a/crazy_functions/解析项目源代码.py b/crazy_functions/解析项目源代码.py index 04e5128..375b87a 100644 --- a/crazy_functions/解析项目源代码.py +++ b/crazy_functions/解析项目源代码.py @@ -12,7 +12,7 @@ def 解析源代码新(file_manifest, project_folder, llm_kwargs, plugin_kwargs, sys_prompt_array = [] report_part_1 = [] - assert len(file_manifest) <= 1024, "源文件太多(超过1024个), 请缩减输入文件的数量。或者,您也可以选择删除此行警告,并修改代码拆分file_manifest列表,从而实现分批次处理。" + assert len(file_manifest) <= 512, "源文件太多(超过512个), 请缩减输入文件的数量。或者,您也可以选择删除此行警告,并修改代码拆分file_manifest列表,从而实现分批次处理。" ############################## <第一步,逐个文件分析,多线程> ################################## for index, fp in enumerate(file_manifest): # 读取文件 diff --git a/crazy_functions/询问多个大语言模型.py b/crazy_functions/询问多个大语言模型.py new file mode 100644 index 0000000..fb78145 --- /dev/null +++ b/crazy_functions/询问多个大语言模型.py @@ -0,0 +1,30 @@ +from toolbox import CatchException, update_ui +from .crazy_utils import request_gpt_model_in_new_thread_with_ui_alive +import datetime +@CatchException +def 同时问询(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port): + """ + txt 输入栏用户输入的文本,例如需要翻译的一段话,再例如一个包含了待处理文件的路径 + llm_kwargs gpt模型参数,如温度和top_p等,一般原样传递下去就行 + plugin_kwargs 插件模型的参数,如温度和top_p等,一般原样传递下去就行 + chatbot 聊天显示框的句柄,用于显示给用户 + history 聊天历史,前情提要 + system_prompt 给gpt的静默提醒 + web_port 当前软件运行的端口号 + """ + history = [] # 清空历史,以免输入溢出 + chatbot.append((txt, "正在同时咨询ChatGPT和ChatGLM……")) + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 由于请求gpt需要一段时间,我们先及时地做一次界面更新 + + # llm_kwargs['llm_model'] = 'chatglm&gpt-3.5-turbo&api2d-gpt-3.5-turbo' # 支持任意数量的llm接口,用&符号分隔 + llm_kwargs['llm_model'] = 'chatglm&gpt-3.5-turbo' # 支持任意数量的llm接口,用&符号分隔 + gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive( + inputs=txt, inputs_show_user=txt, + llm_kwargs=llm_kwargs, chatbot=chatbot, history=history, + sys_prompt=system_prompt, + retry_times_at_unknown_error=0 + ) + + history.append(txt) + history.append(gpt_say) + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 界面更新 \ No newline at end of file diff --git a/docs/Dockerfile+ChatGLM b/docs/Dockerfile+ChatGLM new file mode 100644 index 0000000..dafcee7 --- /dev/null +++ b/docs/Dockerfile+ChatGLM @@ -0,0 +1,59 @@ +# How to build | 如何构建: docker build -t gpt-academic --network=host -f Dockerfile+ChatGLM . +# How to run | 如何运行 (1) 直接运行(选择0号GPU): docker run --rm -it --net=host --gpus="0" gpt-academic +# How to run | 如何运行 (2) 我想运行之前进容器做一些调整: docker run --rm -it --net=host --gpus="0" gpt-academic bash + +# 从NVIDIA源,从而支持显卡运损(检查宿主的nvidia-smi中的cuda版本必须>=11.3) +FROM nvidia/cuda:11.3.1-runtime-ubuntu20.04 +ARG useProxyNetwork='' +RUN apt-get update +RUN apt-get install -y curl proxychains curl +RUN apt-get install -y git python python3 python-dev python3-dev --fix-missing + +# 配置代理网络(构建Docker镜像时使用) +# # comment out below if you do not need proxy network | 如果不需要翻墙 - 从此行向下删除 +RUN $useProxyNetwork curl cip.cc +RUN sed -i '$ d' /etc/proxychains.conf +RUN sed -i '$ d' /etc/proxychains.conf +RUN echo "socks5 127.0.0.1 10880" >> /etc/proxychains.conf +ARG useProxyNetwork=proxychains +# # comment out above if you do not need proxy network | 如果不需要翻墙 - 从此行向上删除 + + +# use python3 as the system default python +RUN curl -sS https://bootstrap.pypa.io/get-pip.py | python3.8 + +# 下载分支 +WORKDIR /gpt +RUN $useProxyNetwork git clone https://github.com/binary-husky/chatgpt_academic.git +WORKDIR /gpt/chatgpt_academic +RUN $useProxyNetwork python3 -m pip install -r requirements.txt +RUN $useProxyNetwork python3 -m pip install -r request_llm/requirements_chatglm.txt +RUN $useProxyNetwork python3 -m pip install torch --extra-index-url https://download.pytorch.org/whl/cu113 + +# 预热CHATGLM参数(非必要 可选步骤) +RUN echo ' \n\ +from transformers import AutoModel, AutoTokenizer \n\ +chatglm_tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True) \n\ +chatglm_model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True).float() ' >> warm_up_chatglm.py +RUN python3 -u warm_up_chatglm.py + +# 禁用缓存,确保更新代码 +ADD "https://www.random.org/cgi-bin/randbyte?nbytes=10&format=h" skipcache +RUN $useProxyNetwork git pull + +# 预热Tiktoken模块 +RUN python3 -c 'from check_proxy import warm_up_modules; warm_up_modules()' + +# 为chatgpt-academic配置代理和API-KEY (非必要 可选步骤) +# 可同时填写多个API-KEY,支持openai的key和api2d的key共存,用英文逗号分割,例如API_KEY = "sk-openaikey1,fkxxxx-api2dkey2,........" +# LLM_MODEL 是选择初始的模型 +# LOCAL_MODEL_DEVICE 是选择chatglm等本地模型运行的设备,可选 cpu 和 cuda +RUN echo ' \n\ +API_KEY = "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,fkxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \n\ +USE_PROXY = True \n\ +LLM_MODEL = "chatglm" \n\ +LOCAL_MODEL_DEVICE = "cuda" \n\ +proxies = { "http": "socks5h://localhost:10880", "https": "socks5h://localhost:10880", } ' >> config_private.py + +# 启动 +CMD ["python3", "-u", "main.py"] diff --git a/img/README_EN.md b/docs/README_EN.md similarity index 100% rename from img/README_EN.md rename to docs/README_EN.md diff --git a/img/demo.jpg b/docs/demo.jpg similarity index 100% rename from img/demo.jpg rename to docs/demo.jpg diff --git a/img/demo2.jpg b/docs/demo2.jpg similarity index 100% rename from img/demo2.jpg rename to docs/demo2.jpg diff --git a/docs/logo.png b/docs/logo.png new file mode 100644 index 0000000..567dee1 Binary files /dev/null and b/docs/logo.png differ diff --git a/docs/self_analysis.md b/docs/self_analysis.md new file mode 100644 index 0000000..28f6682 --- /dev/null +++ b/docs/self_analysis.md @@ -0,0 +1,256 @@ +# chatgpt-academic项目自译解报告 +(Author补充:以下分析均由本项目调用ChatGPT一键生成,如果有不准确的地方,全怪GPT😄) + +## 对程序的整体功能和构架做出概括。然后用一张markdown表格整理每个文件的功能。 + +整体概括: + +该程序是一个基于自然语言处理和机器学习的科学论文辅助工具,主要功能包括聊天机器人、批量总结PDF文档、批量翻译PDF文档、生成函数注释、解析项目源代码等。程序基于 Gradio 构建 Web 服务,并集成了代理和自动更新功能,提高了用户的使用体验。 + +文件功能表格: + +| 文件名 | 文件功能 | +| --- | --- | +| check_proxy.py | 用于检查代理的正确性和可用性 | +| colorful.py | 包含不同预设置颜色的常量,并用于多种UI元素 | +| config.py | 用于全局配置的类 | +| config_private.py | 与config.py文件一起使用的另一个配置文件,用于更改私密信息 | +| core_functional.py | 包含一些TextFunctional类和基础功能函数 | +| crazy_functional.py | 包含大量高级功能函数和实验性的功能函数 | +| main.py | 程序的主入口,包含GUI主窗口和主要的UI管理功能 | +| theme.py | 包含一些预设置主题的颜色 | +| toolbox.py | 提供了一些有用的工具函数 | +| crazy_functions\crazy_utils.py | 包含一些用于实现高级功能的辅助函数 | +| crazy_functions\Latex全文润色.py | 实现了对LaTeX文件中全文的润色和格式化功能 | +| crazy_functions\Latex全文翻译.py | 实现了对LaTeX文件中的内容进行翻译的功能 | +| crazy_functions\_\_init\_\_.py | 用于导入crazy_functional.py中的功能函数 | +| crazy_functions\下载arxiv论文翻译摘要.py | 从Arxiv上下载论文并提取重要信息 | +| crazy_functions\代码重写为全英文_多线程.py | 针对中文Python文件,将其翻译为全英文 | +| crazy_functions\总结word文档.py | 提取Word文件的重要内容来生成摘要 | +| crazy_functions\批量Markdown翻译.py | 批量翻译Markdown文件 | +| crazy_functions\批量总结PDF文档.py | 批量从PDF文件中提取摘要 | +| crazy_functions\批量总结PDF文档pdfminer.py | 批量从PDF文件中提取摘要 | +| crazy_functions\批量翻译PDF文档_多线程.py | 批量翻译PDF文件 | +| crazy_functions\理解PDF文档内容.py | 批量分析PDF文件并提取摘要 | +| crazy_functions\生成函数注释.py | 自动生成Python文件中函数的注释 | +| crazy_functions\解析项目源代码.py | 解析并分析给定项目的源代码 | +| crazy_functions\询问多个大语言模型.py | 向多个大语言模型询问输入文本并进行处理 | +| crazy_functions\读文献写摘要.py | 根据用户输入读取文献内容并生成摘要 | +| crazy_functions\谷歌检索小助手.py | 利用谷歌学术检索用户提供的论文信息并提取相关信息 | +| crazy_functions\高级功能函数模板.py | 实现高级功能的模板函数 | +| request_llm\bridge_all.py | 处理与LLM的交互 | +| request_llm\bridge_chatglm.py | 使用ChatGLM模型进行聊天 | +| request_llm\bridge_chatgpt.py | 实现对话生成的各项功能 | +| request_llm\bridge_tgui.py | 在Websockets中与用户进行交互并生成文本输出 | + + + +## [0/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\check_proxy.py + +该文件主要包括四个函数:check_proxy、backup_and_download、patch_and_restart 和 auto_update。其中,check_proxy 函数用于检查代理是否可用;backup_and_download 用于进行一键更新备份和下载;patch_and_restart 是一键更新协议的重要函数,用于覆盖和重启;auto_update 函数用于查询版本和用户意见,并自动进行一键更新。该文件主要使用了 requests、json、shutil、zipfile、distutils、subprocess 等 Python 标准库和 toolbox 和 colorful 两个第三方库。 + +## [1/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\colorful.py + +该程序文件实现了一些打印文本的函数,使其具有不同的颜色输出。当系统为Linux时直接跳过,否则使用colorama库来实现颜色输出。程序提供了深色和亮色两种颜色输出方式,同时也提供了对打印函数的别名。对于不是终端输出的情况,对所有的打印函数进行重复定义,以便在重定向时能够避免打印错误日志。 + +## [2/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\config.py + +该程序文件是一个配置文件,其主要功能是提供使用API密钥等信息,以及对程序的体验进行优化,例如定义对话框高度、布局等。还包含一些其他的设置,例如设置并行使用的线程数、重试次数限制等等。 + +## [3/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\config_private.py + +这是一个名为config_private.py的Python文件,它用于配置API_KEY和代理信息。API_KEY是一个私密密钥,用于访问某些受保护的API。USE_PROXY变量设置为True以应用代理,proxies变量配置了代理网络的地址和协议。在使用该文件时,需要填写正确的API_KEY和代理信息。 + +## [4/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\core_functional.py + +该文件是一个Python模块,名为"core_functional.py"。模块中定义了一个字典,包含了各种核心功能的配置信息,如英语学术润色、中文学术润色、查找语法错误等。每个功能都包含一些前言和后语,在前言中描述了该功能的任务和要求,在后语中提供一些附加信息。此外,有些功能还定义了一些特定的处理函数和按钮颜色。 + +## [5/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functional.py + +这是一个Python程序文件,文件名是crazy_functional.py。它导入了一个名为HotReload的工具箱,并定义了一个名为get_crazy_functions()的函数。这个函数包括三个部分的插件组,分别是已经编写完成的第一组插件、已经测试但距离完美状态还差一点点的第二组插件和尚未充分测试的第三组插件。每个插件都有一个名称、一个按钮颜色、一个函数和一个是否加入下拉菜单中的标志位。这些插件提供了多种功能,包括生成函数注释、解析项目源代码、批量翻译PDF文档、谷歌检索、PDF文档内容理解和Latex文档的全文润色、翻译等功能。其中第三组插件可能还存在一定的bug。 + +## [6/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\main.py + +该Python脚本代码实现了一个用于交互式对话的Chatbot机器人。它使用了Gradio框架来构建一个Web界面,并在此基础之上嵌入了一个文本输入框和与Chatbot进行交互的其他控件,包括提交、重置、停止和清除按钮、选择框和滑块等。此外,它还包括了一些类和函数和一些用于编程分析的工具和方法。整个程序文件的结构清晰,注释丰富,并提供了很多技术细节,使得开发者可以很容易地在其基础上进行二次开发、修改、扩展和集成。 + +## [7/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\theme.py + +该程序文件名为theme.py,主要功能为调节Gradio的全局样式。在该文件中,调节了Gradio的主题颜色、字体、阴影、边框、渐变等等样式。同时,该文件还添加了一些高级CSS样式,比如调整表格单元格的背景和边框,设定聊天气泡的圆角、最大宽度和阴影等等。如果CODE_HIGHLIGHT为True,则还进行了代码高亮显示。 + +## [8/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\toolbox.py + +这是一个名为`toolbox.py`的源代码文件。该文件包含了一系列工具函数和装饰器,用于聊天Bot的开发和调试。其中有一些功能包括将输入参数进行重组、捕捉函数中的异常并记录到历史记录中、生成Markdown格式的聊天记录报告等。该文件中还包含了一些与转换Markdown文本相关的函数。 + +## [9/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\crazy_utils.py + +这是一个Python程序文件 `crazy_utils.py`,它包含了两个函数: + +- `input_clipping(inputs, history, max_token_limit)`:这个函数接收三个参数,inputs 是一个字符串,history 是一个列表,max_token_limit 是一个整数。它使用 `tiktoken` 、`numpy` 和 `toolbox` 模块,处理输入文本和历史记录,将其裁剪到指定的最大标记数,避免输入过长导致的性能问题。如果 inputs 长度不超过 max_token_limit 的一半,则只裁剪历史;否则,同时裁剪输入和历史。 +- `request_gpt_model_in_new_thread_with_ui_alive(inputs, inputs_show_user, llm_kwargs, chatbot, history, sys_prompt, refresh_interval=0.2, handle_token_exceed=True, retry_times_at_unknown_error=2)`:这个函数接收八个参数,其中后三个是列表类型,其他为标量或句柄等。它提供对话窗口和刷新控制,执行 `predict_no_ui_long_connection` 方法,将输入数据发送至 GPT 模型并获取结果,如果子任务出错,返回相应的错误信息,否则返回结果。 + +## [10/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\Latex全文润色.py + +这是一个名为"crazy_functions\Latex全文润色.py"的程序文件,其中包含了两个函数"Latex英文润色"和"Latex中文润色",以及其他辅助函数。这些函数能够对 Latex 项目进行润色处理,其中 "多文件润色" 函数是一个主要函数,它调用了其他辅助函数用于读取和处理 Latex 项目中的文件。函数使用了多线程和机器学习模型进行自然语言处理,对文件进行简化和排版来满足学术标准。注释已删除并可以在函数内部查找。 + +## [11/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\Latex全文翻译.py + +这个程序文件包括一个用于对整个Latex项目进行翻译的函数 `Latex英译中` 和一个用于将中文翻译为英文的函数 `Latex中译英`。这两个函数都会尝试导入依赖库 tiktoken, 若无法导入则会提示用户安装。`Latex英译中` 函数会对 Latex 项目中的文件进行分离并去除注释,然后运行多线程翻译。`Latex中译英` 也做同样的事情,只不过是将中文翻译为英文。这个程序文件还包括其他一些帮助函数。 + +## [12/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\__init__.py + +这是一个 Python 包,包名为 `crazy_functions`,在 `__init__.py` 文件中定义了一些函数,包含以下函数: + +- `crazy_addition(a, b)`:对两个数进行加法运算,并将结果返回。 +- `crazy_multiplication(a, b)`:对两个数进行乘法运算,并将结果返回。 +- `crazy_subtraction(a, b)`:对两个数进行减法运算,并将结果返回。 +- `crazy_division(a, b)`:对两个数进行除法运算,并将结果返回。 +- `crazy_factorial(n)`:计算 `n` 的阶乘并返回结果。 + +这些函数可能会有一些奇怪或者不符合常规的实现方式(由函数名可以看出来),所以这个包的名称为 `crazy_functions`,可能是暗示这些函数会有一些“疯狂”的实现方式。 + +## [13/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\下载arxiv论文翻译摘要.py + +该程序实现了一个名为“下载arxiv论文并翻译摘要”的函数插件,作者是“binary-husky”。该函数的功能是,在输入一篇arxiv论文的链接后,提取摘要、下载PDF文档、翻译摘要为中文,并将翻译结果保存到文件中。程序使用了一些Python库,如requests、pdfminer和beautifulsoup4等。程序入口是名为“下载arxiv论文并翻译摘要”的函数,其中使用了自定义的辅助函数download_arxiv_和get_name。程序中还使用了其他非函数的辅助函数和变量,如update_ui、CatchException、report_exception和get_conf等。 + +## [14/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\代码重写为全英文_多线程.py + +该文件是一个多线程Python脚本,包含多个函数和利用第三方库进行的API请求。主要功能是将给定文件夹内的Python代码文件中所有中文转化为英文,然后输出转化后的英文代码。重要的功能和步骤包括: + +1. 清空历史,以免输入溢出 +2. 尝试导入依赖,如果缺少依赖,则给出安装建议 +3. 集合文件 +4. 显示随意内容以防卡顿的感觉 +5. Token限制下的截断与处理 +6. 多线程操作请求转换中文变为英文的代码 +7. 所有线程同时开始执行任务函数 +8. 循环轮询各个线程是否执行完毕 +9. 把结果写入文件 +10. 备份一个文件 + +## [15/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\总结word文档.py + +这是一个名为"总结word文档.py"的程序文件,使用python编写。该文件导入了"toolbox"和"crazy_utils"模块,实现了解析docx格式和doc格式的文件的功能。该文件包含了一个名为"解析docx"的函数,通过对文件内容应用自然语言处理技术,生成文章片段的中英文概述。具体实现过程中,该函数使用了"docx"模块和"win32com.client"模块来实现对docx和doc格式文件的解析,同时使用了"request_gpt_model_in_new_thread_with_ui_alive"函数来向GPT模型发起请求。最后,该文件还实现了一个名为"总结word文档"的函数来批量总结Word文档。 + +## [16/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\批量Markdown翻译.py + +这个程序文件实现了一个批量Markdown翻译功能,可以将一个源代码项目中的Markdown文本翻译成指定语言(目前支持中<-英和英<-中)。程序主要分为三个函数,`PaperFileGroup`类用于处理长文本的拆分,`多文件翻译`是主要函数调用了`request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency`函数进行多线程翻译并输出结果,`Markdown英译中`和`Markdown中译外`分别是英译中和中译英的入口函数,用于解析项目路径和调用翻译函数。程序依赖于tiktoken等库实现。 + +## [17/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\批量总结PDF文档.py + +这是一个名为“批量总结PDF文档”的Python脚本,包含了多个函数。其中有一个函数名为“clean_text”,可以对PDF提取出的原始文本进行清洗和格式化处理,将连字转换为其基本形式,并根据heuristic规则判断换行符是否是段落分隔,并相应地进行替换。另一个函数名为“解析PDF”,可以接收一个PDF文件清单,并对清单中的每一个PDF进行解析,提取出文本并调用“clean_text”函数进行清洗和格式化处理,然后向用户发送一个包含文章简介信息的问题并等待用户回答。最后,该脚本也包含一个名为“批量总结PDF文档”的主函数,其中调用了“解析PDF”函数来完成对PDF文件的批量处理。 + +## [18/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\批量总结PDF文档pdfminer.py + +这个文件是一个Python模块,文件名为pdfminer.py,它定义了一个函数批量总结PDF文档。该函数接受一些参数,然后尝试导入pdfminer和beautifulsoup4库。该函数将读取pdf文件或tex文件中的内容,对其进行分析,并使用GPT模型进行自然语言摘要。文件中还有一个辅助函数readPdf,用于读取pdf文件中的内容。 + +## [19/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\批量翻译PDF文档_多线程.py + +这是一个Python脚本,文件名是crazy_functions\批量翻译PDF文档_多线程.py。该脚本提供了一个名为“批量翻译PDF文档”的函数,可以批量翻译PDF文件并生成报告文件。该函数使用了多个模块和函数(如toolbox、crazy_utils、update_ui等),使用了Python的异常处理和多线程功能,还使用了一些文本处理函数和第三方库(如fitz和tiktoken)。在函数执行过程中,它会进行一些参数检查、读取和清理PDF文本、递归地切割PDF文件、获取文章meta信息、多线程翻译、整理报告格式等操作,并更新UI界面和生成报告文件。 + +## [20/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\理解PDF文档内容.py + +这是一个解析PDF文件内容的Python程序,程序文件名为"理解PDF文档内容.py",程序主要由5个步骤组成:第0步是切割PDF文件;第1步是从摘要中提取高价值信息,放到history中;第2步是迭代地历遍整个文章,提取精炼信息;第3步是整理history;第4步是设置一个token上限,防止回答时Token溢出。程序主要用到了Python中的各种模块和函数库,如:toolbox, tiktoken, pymupdf等。 + +## [21/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\生成函数注释.py + +这是一个名为"生成函数注释"的函数,带有一个装饰器"@CatchException",可以捕获异常。该函数接受文件路径、参数和聊天机器人等参数,用于对多个Python或C++文件进行函数注释,使用了"toolbox"和"crazy_utils"模块中的函数。该函数会逐个读取指定文件中的内容,并使用聊天机器人进行交互,向用户请求注释信息,然后将生成的注释与原文件内容一起输出到一个markdown表格中。最后,该函数返回一个字符串,指示任务是否已完成。另外还包含一个名为"批量生成函数注释"的函数,它与"生成函数注释"函数一起用于批量处理多个文件。 + +## [22/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\解析项目源代码.py + +这个程序文件实现了对一个源代码项目进行分析的功能。其中,函数`解析项目本身`、`解析一个Python项目`、`解析一个C项目的头文件`、`解析一个C项目`、`解析一个Java项目`和`解析一个Rect项目`分别用于解析不同类型的项目。函数`解析源代码新`实现了对每一个源代码文件的分析,并将分析结果汇总,同时还实现了分组和迭代处理,提高了效率。最后,函数`write_results_to_file`将所有分析结果写入文件。中间,还用到了`request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency`和`request_gpt_model_in_new_thread_with_ui_alive`来完成请求和响应,并用`update_ui`实时更新界面。 + +## [23/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\询问多个大语言模型.py + +这是一个Python程序,文件名为"crazy_functions\询问多个大语言模型.py"。该程序实现了一个同时向多个大语言模型询问的功能,接收用户输入文本以及模型参数,向ChatGPT和ChatGLM模型发出请求,并将对话记录显示在聊天框中,同时刷新界面。 + +## [24/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\读文章写摘要.py + +该程序文件是一个Python模块,文件名为"读文章写摘要.py",主要包含两个函数:"解析Paper"和"读文章写摘要"。其中,"解析Paper"函数接受文件路径、参数等参数,逐个打印文件内容并使用GPT模型生成对该文件的摘要;"读文章写摘要"函数则接受一段文本内容和参数,将该文本内容及其所有.tex文件逐个传递给"解析Paper"函数进行处理,并使用GPT模型生成文章的中英文摘要。文件还导入了一些工具函数,如异常处理、信息上报和文件写入等。 + +## [25/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\谷歌检索小助手.py + +该文件代码包含了一个名为`get_meta_information`的函数和一个名为`谷歌检索小助手`的装饰器函数,用于从谷歌学术中抓取文章元信息,并从用户提供的搜索页面中分析所有文章的相关信息。该文件使用了许多第三方库,如requests、arxiv、BeautifulSoup等。其中`get_meta_information`函数中还定义了一个名为`string_similar`的辅助函数,用于比较字符串相似度。 + +## [26/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\crazy_functions\高级功能函数模板.py + +该程序文件是一个 Python 模块,包含一个名为“高阶功能模板函数”的函数。该函数接受多个参数,其中包括输入文本、GPT 模型参数、插件模型参数、聊天显示框、聊天历史等。 该函数的主要功能是根据输入文本,使用 GPT 模型生成一些问题,并等待用户回答这些问题(使用 Markdown 格式),然后将用户回答加入到聊天历史中,并更新聊天显示框。该函数还包含了一些异常处理和多线程的相关操作。该程序文件还引用了另一个 Python 模块中的两个函数,分别为“CatchException”和“update_ui”,并且还引用了一个名为“request_gpt_model_in_new_thread_with_ui_alive”的自定义函数。 + +## [27/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\request_llm\bridge_all.py + +这个文件是用来处理与LLM的交互的。包含两个函数,一个是 predict_no_ui_long_connection 用来处理长文本的输出,可以多线程调用;另一个是 predict 用来处理基础的对话功能。这个文件会导入其他文件中定义的方法进行调用,具体调用哪个方法取决于传入的参数。函数中还有一些装饰器和管理多线程的逻辑。 + +## [28/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\request_llm\bridge_chatglm.py + +这个程序文件实现了一个使用ChatGLM模型进行聊天的功能。具体实现过程是:首先进行初始化,然后使用GetGLMHandle类进行ChatGLM模型的加载和运行。predict_no_ui_long_connection函数用于多线程聊天,而predict函数用于单线程聊天,它们的不同之处在于前者不会更新UI界面,后者会。这个文件还导入了其他模块和库,例如transformers、time、importlib等,并使用了多进程Pipe。 + +## [29/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\request_llm\bridge_chatgpt.py + +这个程序文件是用于对话生成的,主要包含三个函数:predict、predict_no_ui、predict_no_ui_long_connection。其中,predict是用于普通对话的函数,具备完备的交互功能,但不具备多线程能力;predict_no_ui是高级实验性功能模块调用的函数,参数简单,可以多线程并行,方便实现复杂的功能逻辑;predict_no_ui_long_connection解决了predict_no_ui在处理长文档时容易断开连接的问题,同样支持多线程。程序中还包含一些常量和工具函数,用于整合信息,选择LLM模型,生成http请求,发送请求,接收响应等。它需要配置一个config文件,包含代理网址、API等敏感信息。 + +## [30/31] 请对下面的程序文件做一个概述: H:\chatgpt_academic_resolve\request_llm\bridge_tgui.py + +该程序文件实现了一个基于Websockets的文本生成服务和对话功能。其中,有三个函数:`run()`、`predict()`和`predict_no_ui_long_connection()`。`run()`函数用于连接到Websocket服务并生成文本结果;`predict()`函数用于将用户输入作为文本生成的输入,同时在UI上显示对话历史记录,并在不断更新UI的过程中不断更新生成的文本输出;`predict_no_ui_long_connection()`函数与`predict()`函数类似,但没有UI,并在一段时间内返回单个生成的文本。整个程序还引入了多个Python模块来完成相关功能,例如`asyncio`、`websockets`、`json`等等。 + +## 根据以上分析,对程序的整体功能和构架重新做出概括。然后用一张markdown表格整理每个文件的功能(包括check_proxy.py, colorful.py, config.py, config_private.py, core_functional.py, crazy_functional.py, main.py, theme.py, toolbox.py, crazy_functions\crazy_utils.py, crazy_functions\Latex全文润色.py, crazy_functions\Latex全文翻译.py, crazy_functions\__init__.py, crazy_functions\下载arxiv论文翻译摘要.py, crazy_functions\代码重写为全英文_多线程.py, crazy_functions\总结word文档.py)。 + +程序功能概括:该程序是一个聊天机器人,可以通过 Web 界面与用户进行交互。它包含了丰富的功能,如文本润色、翻译、代码重写、在线查找等,并且支持多线程处理。用户可以通过 Gradio 框架提供的 Web 界面进行交互,程序还提供了一些调试工具,如toolbox 模块,方便程序开发和调试。 + +下表概述了每个文件的功能: + +| 文件名 | 功能 | +| ----------------------------------------------------------- | ------------------------------------------------------------ | +| check_proxy.py | 检查代理是否可用 | +| colorful.py | 用于打印文本的字体颜色输出模块 | +| config.py | 用于程序中的各种设置,如并行线程数量和重试次数的限制等 | +| config_private.py | 配置API_KEY和代理信息的文件 | +| core_functional.py | 包含具体的文本处理功能的模块 | +| crazy_functional.py | 包括各种插件函数的模块,提供了多种文本处理功能 | +| main.py | 包含 Chatbot 机器人主程序的模块 | +| theme.py | 用于调节全局样式的模块 | +| toolbox.py | 包含工具函数和装饰器,用于聊天Bot的开发和调试 | +| crazy_functions\crazy_utils.py | 包含一些辅助函数,如文本裁剪和消息捕捉等 | +| crazy_functions\Latex全文润色.py | 对 Latex 项目进行润色处理的功能模块 | +| crazy_functions\Latex全文翻译.py | 对 Latex 项目进行翻译的功能模块 | +| crazy_functions\__init__.py | 定义一些奇特的数学函数等 | +| crazy_functions\下载arxiv论文翻译摘要.py | 下载 Arxiv 论文并翻译摘要的功能模块 | +| crazy_functions\代码重写为全英文_多线程.py | 将Python程序中所有中文转化为英文的功能模块 | +| crazy_functions\总结word文档.py | 解析 docx 和 doc 格式的文件,生成文章片段的中英文概述的功能模块 | + +## 根据以上分析,对程序的整体功能和构架重新做出概括。然后用一张markdown表格整理每个文件的功能(包括check_proxy.py, colorful.py, config.py, config_private.py, core_functional.py, crazy_functional.py, main.py, theme.py, toolbox.py, crazy_functions\crazy_utils.py, crazy_functions\Latex全文润色.py, crazy_functions\Latex全文翻译.py, crazy_functions\__init__.py, crazy_functions\下载arxiv论文翻译摘要.py, crazy_functions\代码重写为全英文_多线程.py, crazy_functions\总结word文档.py, crazy_functions\批量Markdown翻译.py, crazy_functions\批量总结PDF文档.py, crazy_functions\批量总结PDF文档pdfminer.py, crazy_functions\批量翻译PDF文档_多线程.py, crazy_functions\理解PDF文档内容.py, crazy_functions\生成函数注释.py, crazy_functions\解析项目源代码.py, crazy_functions\询问多个大语言模型.py, crazy_functions\读文章写摘要.py, crazy_functions\谷歌检索小助手.py, crazy_functions\高级功能函数模板.py, request_llm\bridge_all.py, request_llm\bridge_chatglm.py, request_llm\bridge_chatgpt.py, request_llm\bridge_tgui.py)。 + +根据以上分析,整个程序是一个集成了多个有用工具和功能的文本处理和生成工具,提供了多种在不同场景下使用的功能,包括但不限于对话生成、文本摘要、PDF文件批量处理、代码翻译和实用工具等。主要的Python模块包括"toolbox.py"、"config.py"、"core_functional.py"和"crazy_functional.py"等,并且还使用了许多第三方库和模块实现相关功能。以下是每个程序文件的功能: + +| 文件名 | 文件功能 | +| --- | --- | +| check_proxy.py | 用于检查代理的正确性和可用性 | +| colorful.py | 包含不同预设置颜色的常量,并用于多种UI元素 | +| config.py | 用于全局配置的类 | +| config_private.py | 与config.py文件一起使用的另一个配置文件,用于更改私密信息 | +| core_functional.py | 包含一些TextFunctional类和基础功能函数 | +| crazy_functional.py | 包含大量高级功能函数和实验性的功能函数 | +| main.py | 程序的主入口,包含GUI主窗口和主要的UI管理功能 | +| theme.py | 包含一些预设置主题的颜色 | +| toolbox.py | 提供了一些有用的工具函数 | +| crazy_functions\crazy_utils.py | 包含一些用于实现高级功能的辅助函数 | +| crazy_functions\Latex全文润色.py | 实现了对LaTeX文件中全文的润色和格式化功能 | +| crazy_functions\Latex全文翻译.py | 实现了对LaTeX文件中的内容进行翻译的功能 | +| crazy_functions\_\_init\_\_.py | 用于导入crazy_functional.py中的功能函数 | +| crazy_functions\下载arxiv论文翻译摘要.py | 从Arxiv上下载论文并提取重要信息 | +| crazy_functions\代码重写为全英文_多线程.py | 针对中文Python文件,将其翻译为全英文 | +| crazy_functions\总结word文档.py | 提取Word文件的重要内容来生成摘要 | +| crazy_functions\批量Markdown翻译.py | 批量翻译Markdown文件 | +| crazy_functions\批量总结PDF文档.py | 批量从PDF文件中提取摘要 | +| crazy_functions\批量总结PDF文档pdfminer.py | 批量从PDF文件中提取摘要 | +| crazy_functions\批量翻译PDF文档_多线程.py | 批量翻译PDF文件 | +| crazy_functions\理解PDF文档内容.py | 批量分析PDF文件并提取摘要 | +| crazy_functions\生成函数注释.py | 自动生成Python文件中函数的注释 | +| crazy_functions\解析项目源代码.py | 解析并分析给定项目的源代码 | +| crazy_functions\询问多个大语言模型.py | 向多个大语言模型询问输入文本并进行处理 | +| crazy_functions\读文献写摘要.py | 根据用户输入读取文献内容并生成摘要 | +| crazy_functions\谷歌检索小助手.py | 利用谷歌学术检索用户提供的论文信息并提取相关信息 | +| crazy_functions\高级功能函数模板.py | 实现高级功能的模板函数 | +| request_llm\bridge_all.py | 处理与LLM的交互 | +| request_llm\bridge_chatglm.py | 使用ChatGLM模型进行聊天 | +| request_llm\bridge_chatgpt.py | 实现对话生成的各项功能 | +| request_llm\bridge_tgui.py | 在Websockets中与用户进行交互并生成文本输出 | + diff --git a/main.py b/main.py index 2e4e275..2ee64a9 100644 --- a/main.py +++ b/main.py @@ -1,174 +1,189 @@ import os; os.environ['no_proxy'] = '*' # 避免代理网络产生意外污染 -import gradio as gr -from request_llm.bridge_chatgpt import predict -from toolbox import format_io, find_free_port, on_file_uploaded, on_report_generated, get_conf, ArgsGeneralWrapper, DummyWith -# 建议您复制一个config_private.py放自己的秘密, 如API和代理网址, 避免不小心传github被别人看到 -proxies, WEB_PORT, LLM_MODEL, CONCURRENT_COUNT, AUTHENTICATION, CHATBOT_HEIGHT, LAYOUT, API_KEY = \ - get_conf('proxies', 'WEB_PORT', 'LLM_MODEL', 'CONCURRENT_COUNT', 'AUTHENTICATION', 'CHATBOT_HEIGHT', 'LAYOUT', 'API_KEY') +def main(): + import gradio as gr + from request_llm.bridge_all import predict + from toolbox import format_io, find_free_port, on_file_uploaded, on_report_generated, get_conf, ArgsGeneralWrapper, DummyWith + # 建议您复制一个config_private.py放自己的秘密, 如API和代理网址, 避免不小心传github被别人看到 + proxies, WEB_PORT, LLM_MODEL, CONCURRENT_COUNT, AUTHENTICATION, CHATBOT_HEIGHT, LAYOUT, API_KEY, AVAIL_LLM_MODELS = \ + get_conf('proxies', 'WEB_PORT', 'LLM_MODEL', 'CONCURRENT_COUNT', 'AUTHENTICATION', 'CHATBOT_HEIGHT', 'LAYOUT', 'API_KEY', 'AVAIL_LLM_MODELS') -# 如果WEB_PORT是-1, 则随机选取WEB端口 -PORT = find_free_port() if WEB_PORT <= 0 else WEB_PORT -if not AUTHENTICATION: AUTHENTICATION = None + # 如果WEB_PORT是-1, 则随机选取WEB端口 + PORT = find_free_port() if WEB_PORT <= 0 else WEB_PORT + if not AUTHENTICATION: AUTHENTICATION = None -from check_proxy import get_current_version -initial_prompt = "Serve me as a writing and programming assistant." -title_html = f"

ChatGPT 学术优化 {get_current_version()}

" -description = """代码开源和更新[地址🚀](https://github.com/binary-husky/chatgpt_academic),感谢热情的[开发者们❤️](https://github.com/binary-husky/chatgpt_academic/graphs/contributors)""" + from check_proxy import get_current_version + initial_prompt = "Serve me as a writing and programming assistant." + title_html = f"

ChatGPT 学术优化 {get_current_version()}

" + description = """代码开源和更新[地址🚀](https://github.com/binary-husky/chatgpt_academic),感谢热情的[开发者们❤️](https://github.com/binary-husky/chatgpt_academic/graphs/contributors)""" -# 问询记录, python 版本建议3.9+(越新越好) -import logging -os.makedirs("gpt_log", exist_ok=True) -try:logging.basicConfig(filename="gpt_log/chat_secrets.log", level=logging.INFO, encoding="utf-8") -except:logging.basicConfig(filename="gpt_log/chat_secrets.log", level=logging.INFO) -print("所有问询记录将自动保存在本地目录./gpt_log/chat_secrets.log, 请注意自我隐私保护哦!") + # 问询记录, python 版本建议3.9+(越新越好) + import logging + os.makedirs("gpt_log", exist_ok=True) + try:logging.basicConfig(filename="gpt_log/chat_secrets.log", level=logging.INFO, encoding="utf-8") + except:logging.basicConfig(filename="gpt_log/chat_secrets.log", level=logging.INFO) + print("所有问询记录将自动保存在本地目录./gpt_log/chat_secrets.log, 请注意自我隐私保护哦!") -# 一些普通功能模块 -from core_functional import get_core_functions -functional = get_core_functions() + # 一些普通功能模块 + from core_functional import get_core_functions + functional = get_core_functions() -# 高级函数插件 -from crazy_functional import get_crazy_functions -crazy_fns = get_crazy_functions() + # 高级函数插件 + from crazy_functional import get_crazy_functions + crazy_fns = get_crazy_functions() -# 处理markdown文本格式的转变 -gr.Chatbot.postprocess = format_io + # 处理markdown文本格式的转变 + gr.Chatbot.postprocess = format_io -# 做一些外观色彩上的调整 -from theme import adjust_theme, advanced_css -set_theme = adjust_theme() + # 做一些外观色彩上的调整 + from theme import adjust_theme, advanced_css + set_theme = adjust_theme() -# 代理与自动更新 -from check_proxy import check_proxy, auto_update -proxy_info = check_proxy(proxies) + # 代理与自动更新 + from check_proxy import check_proxy, auto_update, warm_up_modules + proxy_info = check_proxy(proxies) -gr_L1 = lambda: gr.Row().style() -gr_L2 = lambda scale: gr.Column(scale=scale) -if LAYOUT == "TOP-DOWN": - gr_L1 = lambda: DummyWith() - gr_L2 = lambda scale: gr.Row() - CHATBOT_HEIGHT /= 2 + gr_L1 = lambda: gr.Row().style() + gr_L2 = lambda scale: gr.Column(scale=scale) + if LAYOUT == "TOP-DOWN": + gr_L1 = lambda: DummyWith() + gr_L2 = lambda scale: gr.Row() + CHATBOT_HEIGHT /= 2 -cancel_handles = [] -with gr.Blocks(title="ChatGPT 学术优化", theme=set_theme, analytics_enabled=False, css=advanced_css) as demo: - gr.HTML(title_html) - cookies = gr.State({'api_key': API_KEY, 'llm_model': LLM_MODEL}) - with gr_L1(): - with gr_L2(scale=2): - chatbot = gr.Chatbot() - chatbot.style(height=CHATBOT_HEIGHT) - history = gr.State([]) - with gr_L2(scale=1): - with gr.Accordion("输入区", open=True) as area_input_primary: - with gr.Row(): - txt = gr.Textbox(show_label=False, placeholder="Input question here.").style(container=False) - with gr.Row(): - submitBtn = gr.Button("提交", variant="primary") - with gr.Row(): - resetBtn = gr.Button("重置", variant="secondary"); resetBtn.style(size="sm") - stopBtn = gr.Button("停止", variant="secondary"); stopBtn.style(size="sm") - with gr.Row(): - status = gr.Markdown(f"Tip: 按Enter提交, 按Shift+Enter换行。当前模型: {LLM_MODEL} \n {proxy_info}") - with gr.Accordion("基础功能区", open=True) as area_basic_fn: - with gr.Row(): - for k in functional: - variant = functional[k]["Color"] if "Color" in functional[k] else "secondary" - functional[k]["Button"] = gr.Button(k, variant=variant) - with gr.Accordion("函数插件区", open=True) as area_crazy_fn: - with gr.Row(): - gr.Markdown("注意:以下“红颜色”标识的函数插件需从输入区读取路径作为参数.") - with gr.Row(): - for k in crazy_fns: - if not crazy_fns[k].get("AsButton", True): continue - variant = crazy_fns[k]["Color"] if "Color" in crazy_fns[k] else "secondary" - crazy_fns[k]["Button"] = gr.Button(k, variant=variant) - crazy_fns[k]["Button"].style(size="sm") - with gr.Row(): - with gr.Accordion("更多函数插件", open=True): - dropdown_fn_list = [k for k in crazy_fns.keys() if not crazy_fns[k].get("AsButton", True)] - with gr.Column(scale=1): - dropdown = gr.Dropdown(dropdown_fn_list, value=r"打开插件列表", label="").style(container=False) - with gr.Column(scale=1): - switchy_bt = gr.Button(r"请先从插件列表中选择", variant="secondary") - with gr.Row(): - with gr.Accordion("点击展开“文件上传区”。上传本地文件可供红色函数插件调用。", open=False) as area_file_up: - file_upload = gr.Files(label="任何文件, 但推荐上传压缩文件(zip, tar)", file_count="multiple") - with gr.Accordion("展开SysPrompt & 交互界面布局 & Github地址", open=(LAYOUT == "TOP-DOWN")): - system_prompt = gr.Textbox(show_label=True, placeholder=f"System Prompt", label="System prompt", value=initial_prompt) - top_p = gr.Slider(minimum=-0, maximum=1.0, value=1.0, step=0.01,interactive=True, label="Top-p (nucleus sampling)",) - temperature = gr.Slider(minimum=-0, maximum=2.0, value=1.0, step=0.01, interactive=True, label="Temperature",) - checkboxes = gr.CheckboxGroup(["基础功能区", "函数插件区", "底部输入区"], value=["基础功能区", "函数插件区"], label="显示/隐藏功能区") - gr.Markdown(description) - with gr.Accordion("备选输入区", open=True, visible=False) as area_input_secondary: - with gr.Row(): - txt2 = gr.Textbox(show_label=False, placeholder="Input question here.", label="输入区2").style(container=False) - with gr.Row(): - submitBtn2 = gr.Button("提交", variant="primary") - with gr.Row(): - resetBtn2 = gr.Button("重置", variant="secondary"); resetBtn.style(size="sm") - stopBtn2 = gr.Button("停止", variant="secondary"); stopBtn.style(size="sm") - # 功能区显示开关与功能区的互动 - def fn_area_visibility(a): - ret = {} - ret.update({area_basic_fn: gr.update(visible=("基础功能区" in a))}) - ret.update({area_crazy_fn: gr.update(visible=("函数插件区" in a))}) - ret.update({area_input_primary: gr.update(visible=("底部输入区" not in a))}) - ret.update({area_input_secondary: gr.update(visible=("底部输入区" in a))}) - if "底部输入区" in a: ret.update({txt: gr.update(value="")}) - return ret - checkboxes.select(fn_area_visibility, [checkboxes], [area_basic_fn, area_crazy_fn, area_input_primary, area_input_secondary, txt, txt2] ) - # 整理反复出现的控件句柄组合 - input_combo = [cookies, txt, txt2, top_p, temperature, chatbot, history, system_prompt] - output_combo = [cookies, chatbot, history, status] - predict_args = dict(fn=ArgsGeneralWrapper(predict), inputs=input_combo, outputs=output_combo) - # 提交按钮、重置按钮 - cancel_handles.append(txt.submit(**predict_args)) - cancel_handles.append(txt2.submit(**predict_args)) - cancel_handles.append(submitBtn.click(**predict_args)) - cancel_handles.append(submitBtn2.click(**predict_args)) - resetBtn.click(lambda: ([], [], "已重置"), None, [chatbot, history, status]) - resetBtn2.click(lambda: ([], [], "已重置"), None, [chatbot, history, status]) - # 基础功能区的回调函数注册 - for k in functional: - click_handle = functional[k]["Button"].click(fn=ArgsGeneralWrapper(predict), inputs=[*input_combo, gr.State(True), gr.State(k)], outputs=output_combo) - cancel_handles.append(click_handle) - # 文件上传区,接收文件后与chatbot的互动 - file_upload.upload(on_file_uploaded, [file_upload, chatbot, txt], [chatbot, txt]) - # 函数插件-固定按钮区 - for k in crazy_fns: - if not crazy_fns[k].get("AsButton", True): continue - click_handle = crazy_fns[k]["Button"].click(ArgsGeneralWrapper(crazy_fns[k]["Function"]), [*input_combo, gr.State(PORT)], output_combo) + cancel_handles = [] + with gr.Blocks(title="ChatGPT 学术优化", theme=set_theme, analytics_enabled=False, css=advanced_css) as demo: + gr.HTML(title_html) + cookies = gr.State({'api_key': API_KEY, 'llm_model': LLM_MODEL}) + with gr_L1(): + with gr_L2(scale=2): + chatbot = gr.Chatbot() + chatbot.style(height=CHATBOT_HEIGHT) + history = gr.State([]) + with gr_L2(scale=1): + with gr.Accordion("输入区", open=True) as area_input_primary: + with gr.Row(): + txt = gr.Textbox(show_label=False, placeholder="Input question here.").style(container=False) + with gr.Row(): + submitBtn = gr.Button("提交", variant="primary") + with gr.Row(): + resetBtn = gr.Button("重置", variant="secondary"); resetBtn.style(size="sm") + stopBtn = gr.Button("停止", variant="secondary"); stopBtn.style(size="sm") + clearBtn = gr.Button("清除", variant="secondary", visible=False); clearBtn.style(size="sm") + with gr.Row(): + status = gr.Markdown(f"Tip: 按Enter提交, 按Shift+Enter换行。当前模型: {LLM_MODEL} \n {proxy_info}") + with gr.Accordion("基础功能区", open=True) as area_basic_fn: + with gr.Row(): + for k in functional: + variant = functional[k]["Color"] if "Color" in functional[k] else "secondary" + functional[k]["Button"] = gr.Button(k, variant=variant) + with gr.Accordion("函数插件区", open=True) as area_crazy_fn: + with gr.Row(): + gr.Markdown("注意:以下“红颜色”标识的函数插件需从输入区读取路径作为参数.") + with gr.Row(): + for k in crazy_fns: + if not crazy_fns[k].get("AsButton", True): continue + variant = crazy_fns[k]["Color"] if "Color" in crazy_fns[k] else "secondary" + crazy_fns[k]["Button"] = gr.Button(k, variant=variant) + crazy_fns[k]["Button"].style(size="sm") + with gr.Row(): + with gr.Accordion("更多函数插件", open=True): + dropdown_fn_list = [k for k in crazy_fns.keys() if not crazy_fns[k].get("AsButton", True)] + with gr.Column(scale=1): + dropdown = gr.Dropdown(dropdown_fn_list, value=r"打开插件列表", label="").style(container=False) + with gr.Column(scale=1): + switchy_bt = gr.Button(r"请先从插件列表中选择", variant="secondary") + with gr.Row(): + with gr.Accordion("点击展开“文件上传区”。上传本地文件可供红色函数插件调用。", open=False) as area_file_up: + file_upload = gr.Files(label="任何文件, 但推荐上传压缩文件(zip, tar)", file_count="multiple") + with gr.Accordion("更换模型 & SysPrompt & 交互界面布局", open=(LAYOUT == "TOP-DOWN")): + system_prompt = gr.Textbox(show_label=True, placeholder=f"System Prompt", label="System prompt", value=initial_prompt) + top_p = gr.Slider(minimum=-0, maximum=1.0, value=1.0, step=0.01,interactive=True, label="Top-p (nucleus sampling)",) + temperature = gr.Slider(minimum=-0, maximum=2.0, value=1.0, step=0.01, interactive=True, label="Temperature",) + max_length_sl = gr.Slider(minimum=256, maximum=4096, value=512, step=1, interactive=True, label="Local LLM MaxLength",) + checkboxes = gr.CheckboxGroup(["基础功能区", "函数插件区", "底部输入区", "输入清除键"], value=["基础功能区", "函数插件区"], label="显示/隐藏功能区") + md_dropdown = gr.Dropdown(AVAIL_LLM_MODELS, value=LLM_MODEL, label="更换LLM模型/请求源").style(container=False) + + gr.Markdown(description) + with gr.Accordion("备选输入区", open=True, visible=False) as area_input_secondary: + with gr.Row(): + txt2 = gr.Textbox(show_label=False, placeholder="Input question here.", label="输入区2").style(container=False) + with gr.Row(): + submitBtn2 = gr.Button("提交", variant="primary") + with gr.Row(): + resetBtn2 = gr.Button("重置", variant="secondary"); resetBtn2.style(size="sm") + stopBtn2 = gr.Button("停止", variant="secondary"); stopBtn2.style(size="sm") + clearBtn2 = gr.Button("清除", variant="secondary", visible=False); clearBtn.style(size="sm") + # 功能区显示开关与功能区的互动 + def fn_area_visibility(a): + ret = {} + ret.update({area_basic_fn: gr.update(visible=("基础功能区" in a))}) + ret.update({area_crazy_fn: gr.update(visible=("函数插件区" in a))}) + ret.update({area_input_primary: gr.update(visible=("底部输入区" not in a))}) + ret.update({area_input_secondary: gr.update(visible=("底部输入区" in a))}) + ret.update({clearBtn: gr.update(visible=("输入清除键" in a))}) + ret.update({clearBtn2: gr.update(visible=("输入清除键" in a))}) + if "底部输入区" in a: ret.update({txt: gr.update(value="")}) + return ret + checkboxes.select(fn_area_visibility, [checkboxes], [area_basic_fn, area_crazy_fn, area_input_primary, area_input_secondary, txt, txt2, clearBtn, clearBtn2] ) + # 整理反复出现的控件句柄组合 + input_combo = [cookies, max_length_sl, md_dropdown, txt, txt2, top_p, temperature, chatbot, history, system_prompt] + output_combo = [cookies, chatbot, history, status] + predict_args = dict(fn=ArgsGeneralWrapper(predict), inputs=input_combo, outputs=output_combo) + # 提交按钮、重置按钮 + cancel_handles.append(txt.submit(**predict_args)) + cancel_handles.append(txt2.submit(**predict_args)) + cancel_handles.append(submitBtn.click(**predict_args)) + cancel_handles.append(submitBtn2.click(**predict_args)) + resetBtn.click(lambda: ([], [], "已重置"), None, [chatbot, history, status]) + resetBtn2.click(lambda: ([], [], "已重置"), None, [chatbot, history, status]) + clearBtn.click(lambda: ("",""), None, [txt, txt2]) + clearBtn2.click(lambda: ("",""), None, [txt, txt2]) + # 基础功能区的回调函数注册 + for k in functional: + click_handle = functional[k]["Button"].click(fn=ArgsGeneralWrapper(predict), inputs=[*input_combo, gr.State(True), gr.State(k)], outputs=output_combo) + cancel_handles.append(click_handle) + # 文件上传区,接收文件后与chatbot的互动 + file_upload.upload(on_file_uploaded, [file_upload, chatbot, txt, txt2, checkboxes], [chatbot, txt, txt2]) + # 函数插件-固定按钮区 + for k in crazy_fns: + if not crazy_fns[k].get("AsButton", True): continue + click_handle = crazy_fns[k]["Button"].click(ArgsGeneralWrapper(crazy_fns[k]["Function"]), [*input_combo, gr.State(PORT)], output_combo) + click_handle.then(on_report_generated, [file_upload, chatbot], [file_upload, chatbot]) + cancel_handles.append(click_handle) + # 函数插件-下拉菜单与随变按钮的互动 + def on_dropdown_changed(k): + variant = crazy_fns[k]["Color"] if "Color" in crazy_fns[k] else "secondary" + return {switchy_bt: gr.update(value=k, variant=variant)} + dropdown.select(on_dropdown_changed, [dropdown], [switchy_bt] ) + # 随变按钮的回调函数注册 + def route(k, *args, **kwargs): + if k in [r"打开插件列表", r"请先从插件列表中选择"]: return + yield from ArgsGeneralWrapper(crazy_fns[k]["Function"])(*args, **kwargs) + click_handle = switchy_bt.click(route,[switchy_bt, *input_combo, gr.State(PORT)], output_combo) click_handle.then(on_report_generated, [file_upload, chatbot], [file_upload, chatbot]) + # def expand_file_area(file_upload, area_file_up): + # if len(file_upload)>0: return {area_file_up: gr.update(open=True)} + # click_handle.then(expand_file_area, [file_upload, area_file_up], [area_file_up]) cancel_handles.append(click_handle) - # 函数插件-下拉菜单与随变按钮的互动 - def on_dropdown_changed(k): - variant = crazy_fns[k]["Color"] if "Color" in crazy_fns[k] else "secondary" - return {switchy_bt: gr.update(value=k, variant=variant)} - dropdown.select(on_dropdown_changed, [dropdown], [switchy_bt] ) - # 随变按钮的回调函数注册 - def route(k, *args, **kwargs): - if k in [r"打开插件列表", r"请先从插件列表中选择"]: return - yield from ArgsGeneralWrapper(crazy_fns[k]["Function"])(*args, **kwargs) - click_handle = switchy_bt.click(route,[switchy_bt, *input_combo, gr.State(PORT)], output_combo) - click_handle.then(on_report_generated, [file_upload, chatbot], [file_upload, chatbot]) - # def expand_file_area(file_upload, area_file_up): - # if len(file_upload)>0: return {area_file_up: gr.update(open=True)} - # click_handle.then(expand_file_area, [file_upload, area_file_up], [area_file_up]) - cancel_handles.append(click_handle) - # 终止按钮的回调函数注册 - stopBtn.click(fn=None, inputs=None, outputs=None, cancels=cancel_handles) - stopBtn2.click(fn=None, inputs=None, outputs=None, cancels=cancel_handles) -# gradio的inbrowser触发不太稳定,回滚代码到原始的浏览器打开函数 -def auto_opentab_delay(): - import threading, webbrowser, time - print(f"如果浏览器没有自动打开,请复制并转到以下URL:") - print(f"\t(亮色主题): http://localhost:{PORT}") - print(f"\t(暗色主题): http://localhost:{PORT}/?__dark-theme=true") - def open(): - time.sleep(2) # 打开浏览器 - webbrowser.open_new_tab(f"http://localhost:{PORT}/?__dark-theme=true") - threading.Thread(target=open, name="open-browser", daemon=True).start() - threading.Thread(target=auto_update, name="self-upgrade", daemon=True).start() + # 终止按钮的回调函数注册 + stopBtn.click(fn=None, inputs=None, outputs=None, cancels=cancel_handles) + stopBtn2.click(fn=None, inputs=None, outputs=None, cancels=cancel_handles) -auto_opentab_delay() -demo.queue(concurrency_count=CONCURRENT_COUNT).launch(server_name="0.0.0.0", server_port=PORT, auth=AUTHENTICATION) + # gradio的inbrowser触发不太稳定,回滚代码到原始的浏览器打开函数 + def auto_opentab_delay(): + import threading, webbrowser, time + print(f"如果浏览器没有自动打开,请复制并转到以下URL:") + print(f"\t(亮色主题): http://localhost:{PORT}") + print(f"\t(暗色主题): http://localhost:{PORT}/?__dark-theme=true") + def open(): + time.sleep(2) # 打开浏览器 + webbrowser.open_new_tab(f"http://localhost:{PORT}/?__dark-theme=true") + threading.Thread(target=open, name="open-browser", daemon=True).start() + threading.Thread(target=auto_update, name="self-upgrade", daemon=True).start() + threading.Thread(target=warm_up_modules, name="warm-up", daemon=True).start() + + auto_opentab_delay() + demo.queue(concurrency_count=CONCURRENT_COUNT).launch(server_name="0.0.0.0", server_port=PORT, auth=AUTHENTICATION, favicon_path="docs/logo.png") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/request_llm/README.md b/request_llm/README.md index 3277e4a..973adea 100644 --- a/request_llm/README.md +++ b/request_llm/README.md @@ -1,35 +1,53 @@ # 如何使用其他大语言模型(v3.0分支测试中) -## 1. 先运行text-generation +## ChatGLM + +- 安装依赖 `pip install -r request_llm/requirements_chatglm.txt` +- 修改配置,在config.py中将LLM_MODEL的值改为"chatglm" + ``` sh -# 下载模型( text-generation 这么牛的项目,别忘了给人家star ) +LLM_MODEL = "chatglm" +``` +- 运行! +``` sh +`python main.py` +``` + + +--- +## Text-Generation-UI (TGUI) + +### 1. 部署TGUI +``` sh +# 1 下载模型 git clone https://github.com/oobabooga/text-generation-webui.git - -# 安装text-generation的额外依赖 -pip install accelerate bitsandbytes flexgen gradio llamacpp markdown numpy peft requests rwkv safetensors sentencepiece tqdm datasets git+https://github.com/huggingface/transformers - -# 切换路径 +# 2 这个仓库的最新代码有问题,回滚到几周之前 +git reset --hard fcda3f87767e642d1c0411776e549e1d3894843d +# 3 切换路径 cd text-generation-webui - -# 下载模型 +# 4 安装text-generation的额外依赖 +pip install accelerate bitsandbytes flexgen gradio llamacpp markdown numpy peft requests rwkv safetensors sentencepiece tqdm datasets git+https://github.com/huggingface/transformers +# 5 下载模型 python download-model.py facebook/galactica-1.3b # 其他可选如 facebook/opt-1.3b +# facebook/galactica-1.3b # facebook/galactica-6.7b # facebook/galactica-120b # facebook/pygmalion-1.3b 等 # 详情见 https://github.com/oobabooga/text-generation-webui -# 启动text-generation,注意把模型的斜杠改成下划线 -python server.py --cpu --listen --listen-port 7860 --model facebook_galactica-1.3b +# 6 启动text-generation +python server.py --cpu --listen --listen-port 7865 --model facebook_galactica-1.3b ``` -## 2. 修改config.py +### 2. 修改config.py + ``` sh -# LLM_MODEL格式较复杂 TGUI:[模型]@[ws地址]:[ws端口] , 端口要和上面给定的端口一致 -LLM_MODEL = "TGUI:galactica-1.3b@localhost:7860" +# LLM_MODEL格式: tgui:[模型]@[ws地址]:[ws端口] , 端口要和上面给定的端口一致 +LLM_MODEL = "tgui:galactica-1.3b@localhost:7860" ``` -## 3. 运行! +### 3. 运行! ``` sh cd chatgpt-academic python main.py diff --git a/request_llm/bridge_all.py b/request_llm/bridge_all.py new file mode 100644 index 0000000..f1f4ee1 --- /dev/null +++ b/request_llm/bridge_all.py @@ -0,0 +1,210 @@ + +""" + 该文件中主要包含2个函数 + + 不具备多线程能力的函数: + 1. predict: 正常对话时使用,具备完备的交互功能,不可多线程 + + 具备多线程调用能力的函数 + 2. predict_no_ui_long_connection:在实验过程中发现调用predict_no_ui处理长文档时,和openai的连接容易断掉,这个函数用stream的方式解决这个问题,同样支持多线程 +""" +import tiktoken +from functools import wraps, lru_cache +from concurrent.futures import ThreadPoolExecutor + +from .bridge_chatgpt import predict_no_ui_long_connection as chatgpt_noui +from .bridge_chatgpt import predict as chatgpt_ui + +from .bridge_chatglm import predict_no_ui_long_connection as chatglm_noui +from .bridge_chatglm import predict as chatglm_ui + +# from .bridge_tgui import predict_no_ui_long_connection as tgui_noui +# from .bridge_tgui import predict as tgui_ui + +colors = ['#FF00FF', '#00FFFF', '#FF0000', '#990099', '#009999', '#990044'] + +class LazyloadTiktoken(object): + def __init__(self, model): + self.model = model + + @staticmethod + @lru_cache(maxsize=128) + def get_encoder(model): + print('正在加载tokenizer,如果是第一次运行,可能需要一点时间下载参数') + tmp = tiktoken.encoding_for_model(model) + print('加载tokenizer完毕') + return tmp + + def encode(self, *args, **kwargs): + encoder = self.get_encoder(self.model) + return encoder.encode(*args, **kwargs) + + def decode(self, *args, **kwargs): + encoder = self.get_encoder(self.model) + return encoder.decode(*args, **kwargs) + +tokenizer_gpt35 = LazyloadTiktoken("gpt-3.5-turbo") +tokenizer_gpt4 = LazyloadTiktoken("gpt-4") +get_token_num_gpt35 = lambda txt: len(tokenizer_gpt35.encode(txt, disallowed_special=())) +get_token_num_gpt4 = lambda txt: len(tokenizer_gpt4.encode(txt, disallowed_special=())) + +model_info = { + # openai + "gpt-3.5-turbo": { + "fn_with_ui": chatgpt_ui, + "fn_without_ui": chatgpt_noui, + "endpoint": "https://api.openai.com/v1/chat/completions", + "max_token": 4096, + "tokenizer": tokenizer_gpt35, + "token_cnt": get_token_num_gpt35, + }, + + "gpt-4": { + "fn_with_ui": chatgpt_ui, + "fn_without_ui": chatgpt_noui, + "endpoint": "https://api.openai.com/v1/chat/completions", + "max_token": 8192, + "tokenizer": tokenizer_gpt4, + "token_cnt": get_token_num_gpt4, + }, + + # api_2d + "api2d-gpt-3.5-turbo": { + "fn_with_ui": chatgpt_ui, + "fn_without_ui": chatgpt_noui, + "endpoint": "https://openai.api2d.net/v1/chat/completions", + "max_token": 4096, + "tokenizer": tokenizer_gpt35, + "token_cnt": get_token_num_gpt35, + }, + + "api2d-gpt-4": { + "fn_with_ui": chatgpt_ui, + "fn_without_ui": chatgpt_noui, + "endpoint": "https://openai.api2d.net/v1/chat/completions", + "max_token": 8192, + "tokenizer": tokenizer_gpt4, + "token_cnt": get_token_num_gpt4, + }, + + # chatglm + "chatglm": { + "fn_with_ui": chatglm_ui, + "fn_without_ui": chatglm_noui, + "endpoint": None, + "max_token": 1024, + "tokenizer": tokenizer_gpt35, + "token_cnt": get_token_num_gpt35, + }, + +} + + +def LLM_CATCH_EXCEPTION(f): + """ + 装饰器函数,将错误显示出来 + """ + def decorated(inputs, llm_kwargs, history, sys_prompt, observe_window, console_slience): + try: + return f(inputs, llm_kwargs, history, sys_prompt, observe_window, console_slience) + except Exception as e: + from toolbox import get_conf + import traceback + proxies, = get_conf('proxies') + tb_str = '\n```\n' + traceback.format_exc() + '\n```\n' + observe_window[0] = tb_str + return tb_str + return decorated + + +def predict_no_ui_long_connection(inputs, llm_kwargs, history, sys_prompt, observe_window, console_slience=False): + """ + 发送至LLM,等待回复,一次性完成,不显示中间过程。但内部用stream的方法避免中途网线被掐。 + inputs: + 是本次问询的输入 + sys_prompt: + 系统静默prompt + llm_kwargs: + LLM的内部调优参数 + history: + 是之前的对话列表 + observe_window = None: + 用于负责跨越线程传递已经输出的部分,大部分时候仅仅为了fancy的视觉效果,留空即可。observe_window[0]:观测窗。observe_window[1]:看门狗 + """ + import threading, time, copy + + model = llm_kwargs['llm_model'] + n_model = 1 + if '&' not in model: + assert not model.startswith("tgui"), "TGUI不支持函数插件的实现" + + # 如果只询问1个大语言模型: + method = model_info[model]["fn_without_ui"] + return method(inputs, llm_kwargs, history, sys_prompt, observe_window, console_slience) + else: + # 如果同时询问多个大语言模型: + executor = ThreadPoolExecutor(max_workers=4) + models = model.split('&') + n_model = len(models) + + window_len = len(observe_window) + assert window_len==3 + window_mutex = [["", time.time(), ""] for _ in range(n_model)] + [True] + + futures = [] + for i in range(n_model): + model = models[i] + method = model_info[model]["fn_without_ui"] + llm_kwargs_feedin = copy.deepcopy(llm_kwargs) + llm_kwargs_feedin['llm_model'] = model + future = executor.submit(LLM_CATCH_EXCEPTION(method), inputs, llm_kwargs_feedin, history, sys_prompt, window_mutex[i], console_slience) + futures.append(future) + + def mutex_manager(window_mutex, observe_window): + while True: + time.sleep(0.5) + if not window_mutex[-1]: break + # 看门狗(watchdog) + for i in range(n_model): + window_mutex[i][1] = observe_window[1] + # 观察窗(window) + chat_string = [] + for i in range(n_model): + chat_string.append( f"【{str(models[i])} 说】: {window_mutex[i][0]} " ) + res = '

\n\n---\n\n'.join(chat_string) + # # # # # # # # # # # + observe_window[0] = res + + t_model = threading.Thread(target=mutex_manager, args=(window_mutex, observe_window), daemon=True) + t_model.start() + + return_string_collect = [] + while True: + worker_done = [h.done() for h in futures] + if all(worker_done): + executor.shutdown() + break + time.sleep(1) + + for i, future in enumerate(futures): # wait and get + return_string_collect.append( f"【{str(models[i])} 说】: {future.result()} " ) + + window_mutex[-1] = False # stop mutex thread + res = '
\n\n---\n\n'.join(return_string_collect) + return res + + +def predict(inputs, llm_kwargs, *args, **kwargs): + """ + 发送至LLM,流式获取输出。 + 用于基础的对话功能。 + inputs 是本次问询的输入 + top_p, temperature是LLM的内部调优参数 + history 是之前的对话列表(注意无论是inputs还是history,内容太长了都会触发token数量溢出的错误) + chatbot 为WebUI中显示的对话列表,修改它,然后yeild出去,可以直接修改对话界面内容 + additional_fn代表点击的哪个按钮,按钮见functional.py + """ + + method = model_info[llm_kwargs['llm_model']]["fn_with_ui"] + yield from method(inputs, llm_kwargs, *args, **kwargs) + diff --git a/request_llm/bridge_chatglm.py b/request_llm/bridge_chatglm.py new file mode 100644 index 0000000..7af2835 --- /dev/null +++ b/request_llm/bridge_chatglm.py @@ -0,0 +1,140 @@ + +from transformers import AutoModel, AutoTokenizer +import time +import importlib +from toolbox import update_ui, get_conf +from multiprocessing import Process, Pipe + +load_message = "ChatGLM尚未加载,加载需要一段时间。注意,取决于`config.py`的配置,ChatGLM消耗大量的内存(CPU)或显存(GPU),也许会导致低配计算机卡死 ……" + +################################################################################# +class GetGLMHandle(Process): + def __init__(self): + super().__init__(daemon=True) + self.parent, self.child = Pipe() + self.chatglm_model = None + self.chatglm_tokenizer = None + self.info = "" + self.success = True + self.check_dependency() + self.start() + + def check_dependency(self): + try: + import sentencepiece + self.info = "依赖检测通过" + self.success = True + except: + self.info = "缺少ChatGLM的依赖,如果要使用ChatGLM,除了基础的pip依赖以外,您还需要运行`pip install -r request_llm/requirements_chatglm.txt`安装ChatGLM的依赖。" + self.success = False + + def ready(self): + return self.chatglm_model is not None + + def run(self): + # 第一次运行,加载参数 + retry = 0 + while True: + try: + if self.chatglm_model is None: + self.chatglm_tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True) + device, = get_conf('LOCAL_MODEL_DEVICE') + if device=='cpu': + self.chatglm_model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True).float() + else: + self.chatglm_model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True).half().cuda() + self.chatglm_model = self.chatglm_model.eval() + break + else: + break + except: + retry += 1 + if retry > 3: + self.child.send('[Local Message] Call ChatGLM fail 不能正常加载ChatGLM的参数。') + raise RuntimeError("不能正常加载ChatGLM的参数!") + + # 进入任务等待状态 + while True: + kwargs = self.child.recv() + try: + for response, history in self.chatglm_model.stream_chat(self.chatglm_tokenizer, **kwargs): + self.child.send(response) + except: + self.child.send('[Local Message] Call ChatGLM fail.') + self.child.send('[Finish]') + + def stream_chat(self, **kwargs): + self.parent.send(kwargs) + while True: + res = self.parent.recv() + if res != '[Finish]': + yield res + else: + break + return + +global glm_handle +glm_handle = None +################################################################################# +def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="", observe_window=None, console_slience=False): + """ + 多线程方法 + 函数的说明请见 request_llm/bridge_all.py + """ + global glm_handle + if glm_handle is None: + glm_handle = GetGLMHandle() + observe_window[0] = load_message + "\n\n" + glm_handle.info + if not glm_handle.success: + error = glm_handle.info + glm_handle = None + raise RuntimeError(error) + + # chatglm 没有 sys_prompt 接口,因此把prompt加入 history + history_feedin = [] + for i in range(len(history)//2): + history_feedin.append(["What can I do?", sys_prompt] ) + history_feedin.append([history[2*i], history[2*i+1]] ) + + watch_dog_patience = 5 # 看门狗 (watchdog) 的耐心, 设置5秒即可 + response = "" + for response in glm_handle.stream_chat(query=inputs, history=history_feedin, max_length=llm_kwargs['max_length'], top_p=llm_kwargs['top_p'], temperature=llm_kwargs['temperature']): + observe_window[0] = response + if len(observe_window) >= 2: + if (time.time()-observe_window[1]) > watch_dog_patience: + raise RuntimeError("程序终止。") + return response + + + +def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_prompt='', stream = True, additional_fn=None): + """ + 单线程方法 + 函数的说明请见 request_llm/bridge_all.py + """ + chatbot.append((inputs, "")) + + global glm_handle + if glm_handle is None: + glm_handle = GetGLMHandle() + chatbot[-1] = (inputs, load_message + "\n\n" + glm_handle.info) + yield from update_ui(chatbot=chatbot, history=[]) + if not glm_handle.success: + glm_handle = None + return + + if additional_fn is not None: + import core_functional + importlib.reload(core_functional) # 热更新prompt + core_functional = core_functional.get_core_functions() + if "PreProcess" in core_functional[additional_fn]: inputs = core_functional[additional_fn]["PreProcess"](inputs) # 获取预处理函数(如果有的话) + inputs = core_functional[additional_fn]["Prefix"] + inputs + core_functional[additional_fn]["Suffix"] + + history_feedin = [] + for i in range(len(history)//2): + history_feedin.append(["What can I do?", system_prompt] ) + history_feedin.append([history[2*i], history[2*i+1]] ) + + for response in glm_handle.stream_chat(query=inputs, history=history_feedin, max_length=llm_kwargs['max_length'], top_p=llm_kwargs['top_p'], temperature=llm_kwargs['temperature']): + chatbot[-1] = (inputs, response) + yield from update_ui(chatbot=chatbot, history=history) \ No newline at end of file diff --git a/request_llm/bridge_chatgpt.py b/request_llm/bridge_chatgpt.py index e9dfc6b..f10fc20 100644 --- a/request_llm/bridge_chatgpt.py +++ b/request_llm/bridge_chatgpt.py @@ -21,9 +21,9 @@ import importlib # config_private.py放自己的秘密如API和代理网址 # 读取时首先看是否存在私密的config_private配置文件(不受git管控),如果有,则覆盖原config文件 -from toolbox import get_conf, update_ui -proxies, API_URL, API_KEY, TIMEOUT_SECONDS, MAX_RETRY, LLM_MODEL = \ - get_conf('proxies', 'API_URL', 'API_KEY', 'TIMEOUT_SECONDS', 'MAX_RETRY', 'LLM_MODEL') +from toolbox import get_conf, update_ui, is_any_api_key, select_api_key +proxies, API_KEY, TIMEOUT_SECONDS, MAX_RETRY = \ + get_conf('proxies', 'API_KEY', 'TIMEOUT_SECONDS', 'MAX_RETRY') timeout_bot_msg = '[Local Message] Request timeout. Network error. Please check proxy settings in config.py.' + \ '网络错误,检查代理服务器是否可用,以及代理设置的格式是否正确,格式须是[协议]://[地址]:[端口],缺一不可。' @@ -42,17 +42,17 @@ def get_full_error(chunk, stream_response): def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="", observe_window=None, console_slience=False): """ - 发送至chatGPT,等待回复,一次性完成,不显示中间过程。但内部用stream的方法避免中途网线被掐。 - inputs: - 是本次问询的输入 - sys_prompt: - 系统静默prompt - llm_kwargs: - chatGPT的内部调优参数 - history: - 是之前的对话列表 - observe_window = None: - 用于负责跨越线程传递已经输出的部分,大部分时候仅仅为了fancy的视觉效果,留空即可。observe_window[0]:观测窗。observe_window[1]:看门狗 + 发送至chatGPT,等待回复,一次性完成,不显示中间过程。但内部用stream的方法避免中途网线被掐。 + inputs: + 是本次问询的输入 + sys_prompt: + 系统静默prompt + llm_kwargs: + chatGPT的内部调优参数 + history: + 是之前的对话列表 + observe_window = None: + 用于负责跨越线程传递已经输出的部分,大部分时候仅仅为了fancy的视觉效果,留空即可。observe_window[0]:观测窗。observe_window[1]:看门狗 """ watch_dog_patience = 5 # 看门狗的耐心, 设置5秒即可 headers, payload = generate_payload(inputs, llm_kwargs, history, system_prompt=sys_prompt, stream=True) @@ -60,7 +60,9 @@ def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="", while True: try: # make a POST request to the API endpoint, stream=False - response = requests.post(API_URL, headers=headers, proxies=proxies, + 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: retry += 1 @@ -83,6 +85,7 @@ def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="", raise ConnectionAbortedError("OpenAI拒绝了请求:" + error_msg) else: raise RuntimeError("OpenAI拒绝了请求:" + error_msg) + 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 @@ -105,22 +108,22 @@ def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="", def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_prompt='', stream = True, additional_fn=None): """ - 发送至chatGPT,流式获取输出。 - 用于基础的对话功能。 - inputs 是本次问询的输入 - top_p, temperature是chatGPT的内部调优参数 - history 是之前的对话列表(注意无论是inputs还是history,内容太长了都会触发token数量溢出的错误) - chatbot 为WebUI中显示的对话列表,修改它,然后yeild出去,可以直接修改对话界面内容 - additional_fn代表点击的哪个按钮,按钮见functional.py + 发送至chatGPT,流式获取输出。 + 用于基础的对话功能。 + inputs 是本次问询的输入 + top_p, temperature是chatGPT的内部调优参数 + history 是之前的对话列表(注意无论是inputs还是history,内容太长了都会触发token数量溢出的错误) + chatbot 为WebUI中显示的对话列表,修改它,然后yeild出去,可以直接修改对话界面内容 + additional_fn代表点击的哪个按钮,按钮见functional.py """ - if inputs.startswith('sk-') and len(inputs) == 51: + if is_any_api_key(inputs): chatbot._cookies['api_key'] = inputs chatbot.append(("输入已识别为openai的api_key", "api_key已导入")) yield from update_ui(chatbot=chatbot, history=history, msg="api_key已导入") # 刷新界面 return - elif len(chatbot._cookies['api_key']) != 51: + elif not is_any_api_key(chatbot._cookies['api_key']): chatbot.append((inputs, "缺少api_key。\n\n1. 临时解决方案:直接在输入区键入api_key,然后回车提交。\n\n2. 长效解决方案:在config.py中配置。")) - yield from update_ui(chatbot=chatbot, history=history, msg="api_key已导入") # 刷新界面 + yield from update_ui(chatbot=chatbot, history=history, msg="缺少api_key") # 刷新界面 return if additional_fn is not None: @@ -130,20 +133,27 @@ def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_promp if "PreProcess" in core_functional[additional_fn]: inputs = core_functional[additional_fn]["PreProcess"](inputs) # 获取预处理函数(如果有的话) inputs = core_functional[additional_fn]["Prefix"] + inputs + core_functional[additional_fn]["Suffix"] - if stream: - raw_input = inputs - logging.info(f'[raw_input] {raw_input}') - chatbot.append((inputs, "")) - yield from update_ui(chatbot=chatbot, history=history, msg="等待响应") # 刷新界面 + raw_input = inputs + logging.info(f'[raw_input] {raw_input}') + chatbot.append((inputs, "")) + yield from update_ui(chatbot=chatbot, history=history, msg="等待响应") # 刷新界面 - headers, payload = generate_payload(inputs, llm_kwargs, history, system_prompt, stream) + try: + headers, payload = generate_payload(inputs, llm_kwargs, history, system_prompt, stream) + except RuntimeError as e: + chatbot[-1] = (inputs, f"您提供的api-key不满足要求,不包含任何可用于{llm_kwargs['llm_model']}的api-key。") + yield from update_ui(chatbot=chatbot, history=history, msg="api-key不满足要求") # 刷新界面 + return + history.append(inputs); history.append(" ") retry = 0 while True: try: # make a POST request to the API endpoint, stream=True - response = requests.post(API_URL, headers=headers, proxies=proxies, + 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: retry += 1 @@ -160,21 +170,23 @@ def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_promp while True: chunk = next(stream_response) # print(chunk.decode()[6:]) - if is_head_of_the_stream: + if is_head_of_the_stream and (r'"object":"error"' not in chunk.decode()): # 数据流的第一帧不携带content is_head_of_the_stream = False; continue if chunk: try: - if len(json.loads(chunk.decode()[6:])['choices'][0]["delta"]) == 0: + chunk_decoded = chunk.decode() + # 前者API2D的 + if ('data: [DONE]' in chunk_decoded) or (len(json.loads(chunk_decoded[6:])['choices'][0]["delta"]) == 0): # 判定为数据流的结束,gpt_replying_buffer也写完了 logging.info(f'[response] {gpt_replying_buffer}') break # 处理数据流的主体 - chunkjson = json.loads(chunk.decode()[6:]) + chunkjson = json.loads(chunk_decoded[6:]) status_text = f"finish_reason: {chunkjson['choices'][0]['finish_reason']}" # 如果这里抛出异常,一般是文本过长,详情见get_full_error的输出 - gpt_replying_buffer = gpt_replying_buffer + json.loads(chunk.decode()[6:])['choices'][0]["delta"]["content"] + gpt_replying_buffer = gpt_replying_buffer + json.loads(chunk_decoded[6:])['choices'][0]["delta"]["content"] history[-1] = gpt_replying_buffer chatbot[-1] = (history[-2], history[-1]) yield from update_ui(chatbot=chatbot, history=history, msg=status_text) # 刷新界面 @@ -183,7 +195,7 @@ def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_promp traceback.print_exc() yield from update_ui(chatbot=chatbot, history=history, msg="Json解析不合常规") # 刷新界面 chunk = get_full_error(chunk, stream_response) - error_msg = chunk.decode() + error_msg = chunk_decoded if "reduce the length" in error_msg: chatbot[-1] = (chatbot[-1][0], "[Local Message] Reduce the length. 本次输入过长,或历史数据过长. 历史缓存数据现已释放,您可以请再次尝试.") history = [] # 清除历史 @@ -191,23 +203,27 @@ def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_promp chatbot[-1] = (chatbot[-1][0], "[Local Message] Incorrect API key. OpenAI以提供了不正确的API_KEY为由,拒绝服务.") elif "exceeded your current quota" in error_msg: chatbot[-1] = (chatbot[-1][0], "[Local Message] You exceeded your current quota. OpenAI以账户额度不足为由,拒绝服务.") + elif "bad forward key" in error_msg: + chatbot[-1] = (chatbot[-1][0], "[Local Message] Bad forward key. API2D账户额度不足.") else: from toolbox import regular_txt_to_markdown tb_str = '```\n' + traceback.format_exc() + '```' - chatbot[-1] = (chatbot[-1][0], f"[Local Message] 异常 \n\n{tb_str} \n\n{regular_txt_to_markdown(chunk.decode()[4:])}") + chatbot[-1] = (chatbot[-1][0], f"[Local Message] 异常 \n\n{tb_str} \n\n{regular_txt_to_markdown(chunk_decoded[4:])}") 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请求,为发送请求做准备 + 整合所有信息,选择LLM模型,生成http请求,为发送请求做准备 """ - if len(llm_kwargs['api_key']) != 51: + if not is_any_api_key(llm_kwargs['api_key']): raise AssertionError("你提供了错误的API_KEY。\n\n1. 临时解决方案:直接在输入区键入api_key,然后回车提交。\n\n2. 长效解决方案:在config.py中配置。") + api_key = select_api_key(llm_kwargs['api_key'], llm_kwargs['llm_model']) + headers = { "Content-Type": "application/json", - "Authorization": f"Bearer {llm_kwargs['api_key']}" + "Authorization": f"Bearer {api_key}" } conversation_cnt = len(history) // 2 @@ -235,7 +251,7 @@ def generate_payload(inputs, llm_kwargs, history, system_prompt, stream): messages.append(what_i_ask_now) payload = { - "model": llm_kwargs['llm_model'], + "model": llm_kwargs['llm_model'].strip('api2d-'), "messages": messages, "temperature": llm_kwargs['temperature'], # 1.0, "top_p": llm_kwargs['top_p'], # 1.0, diff --git a/request_llm/bridge_tgui.py b/request_llm/bridge_tgui.py index 22a4075..fcf852f 100644 --- a/request_llm/bridge_tgui.py +++ b/request_llm/bridge_tgui.py @@ -13,23 +13,18 @@ import time import threading import importlib from toolbox import get_conf, update_ui -LLM_MODEL, = get_conf('LLM_MODEL') -# "TGUI:galactica-1.3b@localhost:7860" -model_name, addr_port = LLM_MODEL.split('@') -assert ':' in addr_port, "LLM_MODEL 格式不正确!" + LLM_MODEL -addr, port = addr_port.split(':') def random_hash(): letters = string.ascii_lowercase + string.digits return ''.join(random.choice(letters) for i in range(9)) -async def run(context, max_token=512): +async def run(context, max_token, temperature, top_p, addr, port): params = { 'max_new_tokens': max_token, 'do_sample': True, - 'temperature': 0.5, - 'top_p': 0.9, + 'temperature': temperature, + 'top_p': top_p, 'typical_p': 1, 'repetition_penalty': 1.05, 'encoder_repetition_penalty': 1.0, @@ -90,7 +85,7 @@ async def run(context, max_token=512): -def predict_tgui(inputs, top_p, temperature, chatbot, history=[], system_prompt='', stream = True, additional_fn=None): +def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_prompt='', stream = True, additional_fn=None): """ 发送至chatGPT,流式获取输出。 用于基础的对话功能。 @@ -108,18 +103,26 @@ def predict_tgui(inputs, top_p, temperature, chatbot, history=[], system_prompt= inputs = core_functional[additional_fn]["Prefix"] + inputs + core_functional[additional_fn]["Suffix"] raw_input = "What I would like to say is the following: " + inputs - logging.info(f'[raw_input] {raw_input}') history.extend([inputs, ""]) chatbot.append([inputs, ""]) yield from update_ui(chatbot=chatbot, history=history, msg="等待响应") # 刷新界面 - prompt = inputs + prompt = raw_input tgui_say = "" + model_name, addr_port = llm_kwargs['llm_model'].split('@') + assert ':' in addr_port, "LLM_MODEL 格式不正确!" + llm_kwargs['llm_model'] + addr, port = addr_port.split(':') + + mutable = ["", time.time()] def run_coorotine(mutable): async def get_result(mutable): - async for response in run(prompt): + # "tgui:galactica-1.3b@localhost:7860" + + async for response in run(context=prompt, max_token=llm_kwargs['max_length'], + temperature=llm_kwargs['temperature'], + top_p=llm_kwargs['top_p'], addr=addr, port=port): print(response[len(mutable[0]):]) mutable[0] = response if (time.time() - mutable[1]) > 3: @@ -140,28 +143,29 @@ def predict_tgui(inputs, top_p, temperature, chatbot, history=[], system_prompt= chatbot[-1] = (history[-2], history[-1]) yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 - logging.info(f'[response] {tgui_say}') -def predict_tgui_no_ui(inputs, top_p, temperature, history=[], sys_prompt=""): +def predict_no_ui_long_connection(inputs, llm_kwargs, history, sys_prompt, observe_window, console_slience=False): raw_input = "What I would like to say is the following: " + inputs - prompt = inputs + prompt = raw_input tgui_say = "" - mutable = ["", time.time()] - def run_coorotine(mutable): - async def get_result(mutable): - async for response in run(prompt, max_token=20): - print(response[len(mutable[0]):]) - mutable[0] = response - if (time.time() - mutable[1]) > 3: + model_name, addr_port = llm_kwargs['llm_model'].split('@') + assert ':' in addr_port, "LLM_MODEL 格式不正确!" + llm_kwargs['llm_model'] + addr, port = addr_port.split(':') + + + def run_coorotine(observe_window): + async def get_result(observe_window): + async for response in run(context=prompt, max_token=llm_kwargs['max_length'], + temperature=llm_kwargs['temperature'], + top_p=llm_kwargs['top_p'], addr=addr, port=port): + print(response[len(observe_window[0]):]) + observe_window[0] = response + if (time.time() - observe_window[1]) > 5: print('exit when no listener') break - asyncio.run(get_result(mutable)) - thread_listen = threading.Thread(target=run_coorotine, args=(mutable,)) + asyncio.run(get_result(observe_window)) + thread_listen = threading.Thread(target=run_coorotine, args=(observe_window,)) thread_listen.start() - while thread_listen.is_alive(): - time.sleep(1) - mutable[1] = time.time() - tgui_say = mutable[0] - return tgui_say + return observe_window[0] diff --git a/request_llm/requirements_chatglm.txt b/request_llm/requirements_chatglm.txt new file mode 100644 index 0000000..fa049ca --- /dev/null +++ b/request_llm/requirements_chatglm.txt @@ -0,0 +1,6 @@ +protobuf +transformers==4.27.1 +cpm_kernels +torch>=1.10 +mdtex2html +sentencepiece \ No newline at end of file diff --git a/self_analysis.md b/self_analysis.md deleted file mode 100644 index acdcaa7..0000000 --- a/self_analysis.md +++ /dev/null @@ -1,262 +0,0 @@ -# chatgpt-academic项目自译解报告 -(Author补充:以下分析均由本项目调用ChatGPT一键生成,如果有不准确的地方,全怪GPT😄) - -## 对程序的整体功能和构架做出概括。然后用一张markdown表格整理每个文件的功能(包括'check_proxy.py', 'config.py'等)。 - -整体概括: - -该程序是一个基于自然语言处理和机器学习的科学论文辅助工具,主要功能包括聊天机器人、批量总结PDF文档、批量翻译PDF文档、生成函数注释、解析项目源代码等。程序基于 Gradio 构建 Web 服务,并集成了代理和自动更新功能,提高了用户的使用体验。 - -文件功能表格: - -| 文件名称 | 功能 | -| ------------------------------------------------------------ | ------------------------------------------------------------ | -| .\check_proxy.py | 检查代理设置功能。 | -| .\config.py | 配置文件,存储程序的基本设置。 | -| .\config_private.py | 存储代理网络地址的文件。 | -| .\core_functional.py | 主要的程序逻辑,包括聊天机器人和文件处理。 | -| .\cradle.py | 程序入口,初始化程序和启动 Web 服务。 | -| .\crazy_functional.py | 辅助程序功能,包括PDF文档处理、代码处理、函数注释生成等。 | -| .\main.py | 包含聊天机器人的具体实现。 | -| .\show_math.py | 处理 LaTeX 公式的函数。 | -| .\theme.py | 存储 Gradio Web 服务的 CSS 样式文件。 | -| .\toolbox.py | 提供了一系列工具函数,包括文件读写、网页抓取、解析函数参数、生成 HTML 等。 | -| ./crazy_functions/crazy_utils.py | 提供各种工具函数,如解析字符串、清洗文本、清理目录结构等。 | -| ./crazy_functions/\_\_init\_\_.py | crazy_functions 模块的入口文件。 | -| ./crazy_functions/下载arxiv论文翻译摘要.py | 对 arxiv.org 上的 PDF 论文进行下载和翻译。 | -| ./crazy_functions/代码重写为全英文_多线程.py | 将代码文件中的中文注释和字符串替换为英文。 | -| ./crazy_functions/总结word文档.py | 读取 Word 文档并生成摘要。 | -| ./crazy_functions/批量总结PDF文档.py | 批量读取 PDF 文件并生成摘要。 | -| ./crazy_functions/批量总结PDF文档pdfminer.py | 使用 pdfminer 库进行 PDF 文件处理。 | -| ./crazy_functions/批量翻译PDF文档_多线程.py | 使用多线程技术批量翻译 PDF 文件。 | -| ./crazy_functions/生成函数注释.py | 给 Python 函数自动生成说明文档。 | -| ./crazy_functions/解析项目源代码.py | 解析项目中的源代码,提取注释和函数名等信息。 | -| ./crazy_functions/读文章写摘要.py | 读取多个文本文件并生成对应的摘要。 | -| ./crazy_functions/高级功能函数模板.py | 使用 GPT 模型进行文本处理。 | - - - -## [0/22] 程序概述: check_proxy.py - -该程序的文件名是check_proxy.py,主要有两个函数:check_proxy和auto_update。 - -check_proxy函数中会借助requests库向一个IP查询API发送请求,并返回该IP的地理位置信息。同时根据返回的数据来判断代理是否有效。 - -auto_update函数主要用于检查程序更新,会从Github获取程序最新的版本信息,如果当前版本和最新版本相差较大,则会提示用户进行更新。该函数中也会依赖requests库进行网络请求。 - -在程序的开头,还添加了一句防止代理网络影响的代码。程序使用了自己编写的toolbox模块中的get_conf函数来获取代理设置。 - -## [1/22] 程序概述: config.py - -该程序文件是一个Python模块,文件名为config.py。该模块包含了一些变量和配置选项,用于配置一个OpenAI的聊天机器人。具体的配置选项如下: - -- API_KEY: 密钥,用于连接OpenAI的API。需要填写有效的API密钥。 -- USE_PROXY: 是否使用代理。如果需要使用代理,需要将其改为True。 -- proxies: 代理的协议、地址和端口。 -- CHATBOT_HEIGHT: 聊天机器人对话框的高度。 -- LAYOUT: 聊天机器人对话框的布局,默认为左右布局。 -- TIMEOUT_SECONDS: 发送请求到OpenAI后,等待多久判定为超时。 -- WEB_PORT: 网页的端口,-1代表随机端口。 -- MAX_RETRY: 如果OpenAI不响应(网络卡顿、代理失败、KEY失效),重试的次数限制。 -- LLM_MODEL: OpenAI模型选择,目前只对某些用户开放的gpt4。 -- API_URL: OpenAI的API地址。 -- CONCURRENT_COUNT: 使用的线程数。 -- AUTHENTICATION: 用户名和密码,如果需要。 - -## [2/22] 程序概述: config_private.py - -该程序文件名为config_private.py,包含了API_KEY的设置和代理的配置。使用了一个名为API_KEY的常量来存储私人的API密钥。此外,还有一个名为USE_PROXY的常量来标记是否需要使用代理。如果需要代理,则使用了一个名为proxies的字典来存储代理网络的地址,其中包括协议类型、地址和端口。 - -## [3/22] 程序概述: core_functional.py - -该程序文件名为`core_functional.py`,主要是定义了一些核心功能函数,包括英语和中文学术润色、查找语法错误、中译英、学术中英互译、英译中、找图片和解释代码等。每个功能都有一个`Prefix`属性和`Suffix`属性,`Prefix`是指在用户输入的任务前面要显示的文本,`Suffix`是指在任务后面要显示的文本。此外,还有一个`Color`属性指示按钮的颜色,以及一个`PreProcess`函数表示对输入进行预处理的函数。 - -## [4/22] 程序概述: cradle.py - -该程序文件名为cradle.py,主要功能是检测当前版本与远程最新版本是否一致,如果不一致则输出新版本信息并提示更新。其流程大致如下: - -1. 导入相关模块与自定义工具箱函数get_conf -2. 读取配置文件中的代理proxies -3. 使用requests模块请求远程版本信息(url为https://raw.githubusercontent.com/binary-husky/chatgpt_academic/master/version)并加载为json格式 -4. 获取远程版本号、是否显示新功能信息、新功能内容 -5. 读取本地版本文件version并加载为json格式 -6. 获取当前版本号 -7. 比较当前版本与远程版本,如果远程版本号比当前版本号高0.05以上,则输出新版本信息并提示更新 -8. 如果不需要更新,则直接返回 - -## [5/22] 程序概述: crazy_functional.py - -该程序文件名为.\crazy_functional.py,主要定义了一个名为get_crazy_functions()的函数,该函数返回一个字典类型的变量function_plugins,其中包含了一些函数插件。 - -一些重要的函数插件包括: - -- 读文章写摘要:可以自动读取Tex格式的论文,并生成其摘要。 - -- 批量生成函数注释:可以批量生成Python函数的文档注释。 - -- 解析项目源代码:可以解析Python、C++、Golang、Java及React项目的源代码。 - -- 批量总结PDF文档:可以对PDF文档进行批量总结,以提取其中的关键信息。 - -- 一键下载arxiv论文并翻译摘要:可以自动下载arxiv.org网站上的PDF论文,并翻译生成其摘要。 - -- 批量翻译PDF文档(多线程):可以对PDF文档进行批量翻译,并使用多线程方式提高翻译效率。 - -## [6/22] 程序概述: main.py - -本程序为一个基于 Gradio 和 GPT-3 的交互式聊天机器人,文件名为 main.py。其中主要功能包括: - -1. 使用 Gradio 建立 Web 界面,实现用户与聊天机器人的交互; -2. 通过 bridge_chatgpt 模块,利用 GPT-3 模型实现聊天机器人的逻辑; -3. 提供一些基础功能和高级函数插件,用户可以通过按钮选择使用; -4. 提供文档格式转变、外观调整以及代理和自动更新等功能。 - -程序的主要流程为: - -1. 导入所需的库和模块,并通过 get_conf 函数获取配置信息; -2. 设置 Gradio 界面的各个组件,包括聊天窗口、输入区、功能区、函数插件区等; -3. 注册各个组件的回调函数,包括用户输入、信号按钮等,实现机器人逻辑的交互; -4. 通过 Gradio 的 queue 函数和 launch 函数启动 Web 服务,并提供聊天机器人的功能。 - -此外,程序还提供了代理和自动更新功能,可以确保用户的使用体验。 - -## [7/22] 程序概述: show_math.py - -该程序是一个Python脚本,文件名为show_math.py。它转换Markdown和LaTeX混合语法到带MathML的HTML。程序使用latex2mathml模块来实现从LaTeX到MathML的转换,将符号转换为HTML实体以批量处理。程序利用正则表达式和递归函数的方法处理不同形式的LaTeX语法,支持以下四种情况:$$形式、$形式、\[..]形式和\(...\)形式。如果无法转换某个公式,则在该位置插入一条错误消息。最后,程序输出HTML字符串。 - -## [8/22] 程序概述: theme.py - -该程序文件为一个Python脚本,其功能是调整Gradio应用的主题和样式,包括字体、颜色、阴影、背景等等。在程序中,使用了Gradio提供的默认颜色主题,并针对不同元素设置了相应的样式属性,以达到美化显示的效果。此外,程序中还包含了一段高级CSS样式代码,针对表格、列表、聊天气泡、行内代码等元素进行了样式设定。 - -## [9/22] 程序概述: toolbox.py - -此程序文件主要包含了一系列用于聊天机器人开发的实用工具函数和装饰器函数。主要函数包括: - -1. ArgsGeneralWrapper:一个装饰器函数,用于重组输入参数,改变输入参数的顺序与结构。 - -2. get_reduce_token_percent:一个函数,用于计算自然语言处理时会出现的token溢出比例。 - -3. predict_no_ui_but_counting_down:一个函数,调用聊天接口,并且保留了一定的界面心跳功能,即当对话太长时,会自动采用二分法截断。 - -4. write_results_to_file:一个函数,将对话记录history生成Markdown格式的文本,并写入文件中。 - -5. regular_txt_to_markdown:一个函数,将普通文本转换为Markdown格式的文本。 - -6. CatchException:一个装饰器函数,捕捉函数调度中的异常,并封装到一个生成器中返回,并显示到聊天当中。 - -7. HotReload:一个装饰器函数,实现函数插件的热更新。 - -8. report_execption:一个函数,向chatbot中添加错误信息。 - -9. text_divide_paragraph:一个函数,将文本按照段落分隔符分割开,生成带有段落标签的HTML代码。 - -10. markdown_convertion:一个函数,将Markdown格式的文本转换为HTML格式。如果包含数学公式,则先将公式转换为HTML格式。 - -11. close_up_code_segment_during_stream:一个函数,用于在gpt输出代码的中途,即输出了前面的```,但还没输出完后面的```,补上后面的```。 - -12. format_io:一个函数,将输入和输出解析为HTML格式。将输出部分的Markdown和数学公式转换为HTML格式。 - -13. find_free_port:一个函数,返回当前系统中可用的未使用端口。 - -14. extract_archive:一个函数,解压缩文件。 - -15. find_recent_files:一个函数,查找目录下一分钟内创建的文件。 - -16. on_file_uploaded:一个函数,响应用户上传的文件。 - -## [10/22] 程序概述: crazy_functions\crazy_utils.py - -这是一个名为"crazy_utils.py"的Python程序文件,包含了两个函数: -1. `breakdown_txt_to_satisfy_token_limit()`:接受文本字符串、计算文本单词数量的函数和单词数量限制作为输入参数,将长文本拆分成合适的长度,以满足单词数量限制。这个函数使用一个递归方法去拆分长文本。 -2. `breakdown_txt_to_satisfy_token_limit_for_pdf()`:类似于`breakdown_txt_to_satisfy_token_limit()`,但是它使用一个不同的递归方法来拆分长文本,以满足PDF文档中的需求。当出现无法继续拆分的情况时,该函数将使用一个中文句号标记插入文本来截断长文本。如果还是无法拆分,则会引发运行时异常。 - -## [11/22] 程序概述: crazy_functions\__init__.py - -这个程序文件是一个 Python 的包,包名为 "crazy_functions",并且是其中的一个子模块 "__init__.py"。该包中可能包含多个函数或类,用于实现各种疯狂的功能。由于该文件的具体代码没有给出,因此无法进一步确定该包中的功能。通常情况下,一个包应该具有 __init__.py、__main__.py 和其它相关的模块文件,用于实现该包的各种功能。 - -## [12/22] 程序概述: crazy_functions\下载arxiv论文翻译摘要.py - -这个程序实现的功能是下载arxiv论文并翻译摘要,文件名为`下载arxiv论文翻译摘要.py`。这个程序引入了`requests`、`unicodedata`、`os`、`re`等Python标准库,以及`pdfminer`、`bs4`等第三方库。其中`download_arxiv_`函数主要实现了从arxiv网站下载论文的功能,包括解析链接、获取论文信息、下载论文和生成文件名等,`get_name`函数则是为了从arxiv网站中获取论文信息创建的辅助函数。`下载arxiv论文并翻译摘要`函数则是实现了从下载好的PDF文件中提取摘要,然后使用预先训练的GPT模型翻译为中文的功能。同时,该函数还会将历史记录写入文件中。函数还会通过`CatchException`函数来捕获程序中出现的异常信息。 - -## [13/22] 程序概述: crazy_functions\代码重写为全英文_多线程.py - -该程序文件为一个Python多线程程序,文件名为"crazy_functions\代码重写为全英文_多线程.py"。该程序使用了多线程技术,将一个大任务拆成多个小任务,同时执行,提高运行效率。 - -程序的主要功能是将Python文件中的中文转换为英文,同时将转换后的代码输出。程序先清空历史记录,然后尝试导入openai和transformers等依赖库。程序接下来会读取当前路径下的.py文件和crazy_functions文件夹中的.py文件,并将其整合成一个文件清单。随后程序会使用GPT2模型进行中英文的翻译,并将结果保存在本地路径下的"gpt_log/generated_english_version"文件夹中。程序最终会生成一个任务执行报告。 - -需要注意的是,该程序依赖于"request_llm"和"toolbox"库以及本地的"crazy_utils"模块。 - -## [14/22] 程序概述: crazy_functions\总结word文档.py - -该程序文件是一个 Python 脚本文件,文件名为 ./crazy_functions/总结word文档.py。该脚本是一个函数插件,提供了名为“总结word文档”的函数。该函数的主要功能是批量读取给定文件夹下的 Word 文档文件,并使用 GPT 模型生成对每个文件的概述和意见建议。其中涉及到了读取 Word 文档、使用 GPT 模型等操作,依赖于许多第三方库。该文件也提供了导入依赖的方法,使用该脚本需要安装依赖库 python-docx 和 pywin32。函数功能实现的过程中,使用了一些用于调试的变量(如 fast_debug),可在需要时设置为 True。该脚本文件也提供了对程序功能和贡献者的注释。 - -## [15/22] 程序概述: crazy_functions\批量总结PDF文档.py - -该程序文件名为 `./crazy_functions\批量总结PDF文档.py`,主要实现了批量处理PDF文档的功能。具体实现了以下几个函数: - -1. `is_paragraph_break(match)`:根据给定的匹配结果判断换行符是否表示段落分隔。 -2. `normalize_text(text)`:通过将文本特殊符号转换为其基本形式来对文本进行归一化处理。 -3. `clean_text(raw_text)`:对从 PDF 提取出的原始文本进行清洗和格式化处理。 -4. `解析PDF(file_manifest, project_folder, top_p, temperature, chatbot, history, systemPromptTxt)`:对给定的PDF文件进行分析并生成相应的概述。 -5. `批量总结PDF文档(txt, top_p, temperature, chatbot, history, systemPromptTxt, WEB_PORT)`:批量处理PDF文件,对其进行摘要生成。 - -其中,主要用到了第三方库`pymupdf`对PDF文件进行处理。程序通过调用`fitz.open`函数打开PDF文件,使用`page.get_text()`方法获取PDF文本内容。然后,使用`clean_text`函数对文本进行清洗和格式化处理,生成最终的摘要。最后,调用`write_results_to_file`函数将历史记录写入文件并输出。 - -## [16/22] 程序概述: crazy_functions\批量总结PDF文档pdfminer.py - -这个程序文件名是./crazy_functions\批量总结PDF文档pdfminer.py,是一个用于批量读取PDF文件,解析其中的内容,并对其进行概括的程序。程序中引用了pdfminer和beautifulsoup4等Python库,读取PDF文件并将其转化为文本内容,然后利用GPT模型生成摘要语言,最终输出一个中文和英文的摘要。程序还有一些错误处理的代码,会输出错误信息。 - -## [17/22] 程序概述: crazy_functions\批量翻译PDF文档_多线程.py - -这是一个 Python 程序文件,文件名为 `批量翻译PDF文档_多线程.py`,包含多个函数。主要功能是批量处理 PDF 文档,解析其中的文本,进行清洗和格式化处理,并使用 OpenAI 的 GPT 模型进行翻译。其中使用了多线程技术来提高程序的效率和并行度。 - -## [18/22] 程序概述: crazy_functions\生成函数注释.py - -该程序文件名为./crazy_functions\生成函数注释.py。该文件包含两个函数,分别为`生成函数注释`和`批量生成函数注释`。 - -函数`生成函数注释`包含参数`file_manifest`、`project_folder`、`top_p`、`temperature`、`chatbot`、`history`和`systemPromptTxt`。其中,`file_manifest`为一个包含待处理文件路径的列表,`project_folder`表示项目文件夹路径,`top_p`和`temperature`是GPT模型参数,`chatbot`为与用户交互的聊天机器人,`history`记录聊天机器人与用户的历史记录,`systemPromptTxt`为聊天机器人发送信息前的提示语。`生成函数注释`通过读取文件内容,并调用GPT模型对文件中的所有函数生成注释,最后使用markdown表格输出结果。函数中还包含一些条件判断和计时器,以及调用其他自定义模块的函数。 - -函数`批量生成函数注释`包含参数`txt`、`top_p`、`temperature`、`chatbot`、`history`、`systemPromptTxt`和`WEB_PORT`。其中,`txt`表示用户输入的项目文件夹路径,其他参数含义与`生成函数注释`中相同。`批量生成函数注释`主要是通过解析项目文件夹,获取所有待处理文件的路径,并调用函数`生成函数注释`对每个文件进行处理,最终生成注释表格输出给用户。 - -## [19/22] 程序概述: crazy_functions\解析项目源代码.py - -该程序文件包含了多个函数,用于解析不同类型的项目,如Python项目、C项目、Java项目等。其中,最核心的函数是`解析源代码()`,它会对给定的一组文件进行分析,并返回对应的结果。具体流程如下: - -1. 遍历所有待分析的文件,对每个文件进行如下处理: - - 1.1 从文件中读取代码内容,构造成一个字符串。 - - 1.2 构造一条GPT请求,向`predict_no_ui_but_counting_down()`函数发送请求,等待GPT回复。 - - 1.3 将GPT回复添加到机器人会话列表中,更新历史记录。 - - 1.4 如果不是快速调试模式,则等待2秒钟,继续分析下一个文件。 - -2. 如果所有文件都分析完成,则向机器人会话列表中添加一条新消息,提示用户整个分析过程已经结束。 - -3. 返回机器人会话列表和历史记录。 - -除此之外,该程序文件还定义了若干个函数,用于针对不同类型的项目进行解析。这些函数会按照不同的方式调用`解析源代码()`函数。例如,对于Python项目,只需要分析.py文件;对于C项目,需要同时分析.h和.cpp文件等。每个函数中都会首先根据给定的项目路径读取相应的文件,然后调用`解析源代码()`函数进行分析。 - -## [20/22] 程序概述: crazy_functions\读文章写摘要.py - -该程序文件为一个名为“读文章写摘要”的Python函数,用于解析项目文件夹中的.tex文件,并使用GPT模型生成文章的中英文摘要。函数使用了request_llm.bridge_chatgpt和toolbox模块中的函数,并包含两个子函数:解析Paper和CatchException。函数参数包括txt,top_p,temperature,chatbot,history,systemPromptTxt和WEB_PORT。执行过程中函数首先清空历史,然后根据项目文件夹中的.tex文件列表,对每个文件调用解析Paper函数生成中文摘要,最后根据所有文件的中文摘要,调用GPT模型生成英文摘要。函数运行过程中会将结果写入文件并返回聊天机器人和历史记录。 - -## [21/22] 程序概述: crazy_functions\高级功能函数模板.py - -该程序文件为一个高级功能函数模板,文件名为"./crazy_functions\高级功能函数模板.py"。 - -该文件导入了两个模块,分别是"request_llm.bridge_chatgpt"和"toolbox"。其中"request_llm.bridge_chatgpt"模块包含了一个函数"predict_no_ui_long_connection",该函数用于请求GPT模型进行对话生成。"toolbox"模块包含了三个函数,分别是"catchException"、"report_exception"和"write_results_to_file"函数,这三个函数主要用于异常处理和日志记录等。 - -该文件定义了一个名为"高阶功能模板函数"的函数,并通过"decorator"装饰器将该函数装饰为一个异常处理函数,可以处理函数执行过程中出现的错误。该函数的作用是生成历史事件查询的问题,并向用户询问历史中哪些事件发生在指定日期,并索要相关图片。在查询完所有日期后,该函数返回所有历史事件及其相关图片的列表。其中,该函数的输入参数包括: - -1. txt: 一个字符串,表示当前消息的文本内容。 -2. top_p: 一个浮点数,表示GPT模型生成文本时的"top_p"参数。 -3. temperature: 一个浮点数,表示GPT模型生成文本时的"temperature"参数。 -4. chatbot: 一个列表,表示当前对话的记录列表。 -5. history: 一个列表,表示当前对话的历史记录列表。 -6. systemPromptTxt: 一个字符串,表示当前对话的系统提示信息。 -7. WEB_PORT: 一个整数,表示当前应用程序的WEB端口号。 - -该函数在执行过程中,会先清空历史记录,以免输入溢出。然后,它会循环5次,生成5个历史事件查询的问题,并向用户请求输入相关信息。每次询问不携带之前的询问历史。在生成每个问题时,该函数会向"chatbot"列表中添加一条消息记录,并设置该记录的初始状态为"[Local Message] waiting gpt response."。然后,该函数会调用"predict_no_ui_long_connection"函数向GPT模型请求生成一段文本,并将生成的文本作为回答。如果请求过程中出现异常,该函数会忽略异常。最后,该函数将问题和回答添加到"chatbot"列表和"history"列表中,并将"chatbot"和"history"列表作为函数的返回值返回。 - diff --git a/toolbox.py b/toolbox.py index 5bbe954..038d7be 100644 --- a/toolbox.py +++ b/toolbox.py @@ -1,13 +1,10 @@ import markdown -import mdtex2html -import threading import importlib import traceback import inspect import re from latex2mathml.converter import convert as tex2mathml from functools import wraps, lru_cache - ############################### 插件输入输出接驳区 ####################################### class ChatBotWithCookies(list): def __init__(self, cookie): @@ -25,9 +22,9 @@ class ChatBotWithCookies(list): def ArgsGeneralWrapper(f): """ - 装饰器函数,用于重组输入参数,改变输入参数的顺序与结构。 + 装饰器函数,用于重组输入参数,改变输入参数的顺序与结构。 """ - def decorated(cookies, txt, txt2, top_p, temperature, chatbot, history, system_prompt, *args): + def decorated(cookies, max_length, llm_model, txt, txt2, top_p, temperature, chatbot, history, system_prompt, *args): txt_passon = txt if txt == "" and txt2 != "": txt_passon = txt2 # 引入一个有cookie的chatbot @@ -37,8 +34,9 @@ def ArgsGeneralWrapper(f): }) llm_kwargs = { 'api_key': cookies['api_key'], - 'llm_model': cookies['llm_model'], + 'llm_model': llm_model, 'top_p':top_p, + 'max_length': max_length, 'temperature':temperature, } plugin_kwargs = { @@ -55,129 +53,10 @@ def update_ui(chatbot, history, msg='正常', **kwargs): # 刷新界面 """ assert isinstance(chatbot, ChatBotWithCookies), "在传递chatbot的过程中不要将其丢弃。必要时,可用clear将其清空,然后用for+append循环重新赋值。" yield chatbot.get_cookies(), chatbot, history, msg -############################### ################## ####################################### -########################################################################################## - -def get_reduce_token_percent(text): - """ - * 此函数未来将被弃用 - """ - try: - # text = "maximum context length is 4097 tokens. However, your messages resulted in 4870 tokens" - pattern = r"(\d+)\s+tokens\b" - match = re.findall(pattern, text) - EXCEED_ALLO = 500 # 稍微留一点余地,否则在回复时会因余量太少出问题 - max_limit = float(match[0]) - EXCEED_ALLO - current_tokens = float(match[1]) - ratio = max_limit/current_tokens - assert ratio > 0 and ratio < 1 - return ratio, str(int(current_tokens-max_limit)) - except: - return 0.5, '不详' - -def predict_no_ui_but_counting_down(i_say, i_say_show_user, chatbot, llm_kwargs, history=[], sys_prompt='', long_connection=True): - """ - * 此函数未来将被弃用(替代函数 request_gpt_model_in_new_thread_with_ui_alive 文件 chatgpt_academic/crazy_functions/crazy_utils) - - 调用简单的predict_no_ui接口,但是依然保留了些许界面心跳功能,当对话太长时,会自动采用二分法截断 - i_say: 当前输入 - i_say_show_user: 显示到对话界面上的当前输入,例如,输入整个文件时,你绝对不想把文件的内容都糊到对话界面上 - chatbot: 对话界面句柄 - top_p, temperature: gpt参数 - history: gpt参数 对话历史 - sys_prompt: gpt参数 sys_prompt - long_connection: 是否采用更稳定的连接方式(推荐)(已弃用) - """ - import time - from request_llm.bridge_chatgpt import predict_no_ui_long_connection - from toolbox import get_conf - TIMEOUT_SECONDS, MAX_RETRY = get_conf('TIMEOUT_SECONDS', 'MAX_RETRY') - # 多线程的时候,需要一个mutable结构在不同线程之间传递信息 - # list就是最简单的mutable结构,我们第一个位置放gpt输出,第二个位置传递报错信息 - mutable = [None, ''] - # multi-threading worker - - def mt(i_say, history): - while True: - try: - mutable[0] = predict_no_ui_long_connection( - inputs=i_say, llm_kwargs=llm_kwargs, history=history, sys_prompt=sys_prompt) - - except ConnectionAbortedError as token_exceeded_error: - # 尝试计算比例,尽可能多地保留文本 - p_ratio, n_exceed = get_reduce_token_percent( - str(token_exceeded_error)) - if len(history) > 0: - history = [his[int(len(his) * p_ratio):] - for his in history if his is not None] - else: - i_say = i_say[: int(len(i_say) * p_ratio)] - mutable[1] = f'警告,文本过长将进行截断,Token溢出数:{n_exceed},截断比例:{(1-p_ratio):.0%}。' - except TimeoutError as e: - mutable[0] = '[Local Message] 请求超时。' - raise TimeoutError - except Exception as e: - mutable[0] = f'[Local Message] 异常:{str(e)}.' - raise RuntimeError(f'[Local Message] 异常:{str(e)}.') - # 创建新线程发出http请求 - thread_name = threading.Thread(target=mt, args=(i_say, history)) - thread_name.start() - # 原来的线程则负责持续更新UI,实现一个超时倒计时,并等待新线程的任务完成 - cnt = 0 - while thread_name.is_alive(): - cnt += 1 - chatbot[-1] = (i_say_show_user, - f"[Local Message] {mutable[1]}waiting gpt response {cnt}/{TIMEOUT_SECONDS*2*(MAX_RETRY+1)}"+''.join(['.']*(cnt % 4))) - yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 - time.sleep(1) - # 把gpt的输出从mutable中取出来 - gpt_say = mutable[0] - if gpt_say == '[Local Message] Failed with timeout.': - raise TimeoutError - return gpt_say - - -def write_results_to_file(history, file_name=None): - """ - 将对话记录history以Markdown格式写入文件中。如果没有指定文件名,则使用当前时间生成文件名。 - """ - import os - import time - if file_name is None: - # file_name = time.strftime("chatGPT分析报告%Y-%m-%d-%H-%M-%S", time.localtime()) + '.md' - file_name = 'chatGPT分析报告' + \ - time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + '.md' - os.makedirs('./gpt_log/', exist_ok=True) - with open(f'./gpt_log/{file_name}', 'w', encoding='utf8') as f: - f.write('# chatGPT 分析报告\n') - for i, content in enumerate(history): - try: # 这个bug没找到触发条件,暂时先这样顶一下 - if type(content) != str: - content = str(content) - except: - continue - if i % 2 == 0: - f.write('## ') - f.write(content) - f.write('\n\n') - res = '以上材料已经被写入' + os.path.abspath(f'./gpt_log/{file_name}') - print(res) - return res - - -def regular_txt_to_markdown(text): - """ - 将普通文本转换为Markdown格式的文本。 - """ - text = text.replace('\n', '\n\n') - text = text.replace('\n\n\n', '\n\n') - text = text.replace('\n\n\n', '\n\n') - return text - def CatchException(f): """ - 装饰器函数,捕捉函数f中的异常并封装到一个生成器中返回,并显示到聊天当中。 + 装饰器函数,捕捉函数f中的异常并封装到一个生成器中返回,并显示到聊天当中。 """ @wraps(f) def decorated(txt, top_p, temperature, chatbot, history, systemPromptTxt, WEB_PORT): @@ -214,9 +93,70 @@ def HotReload(f): return decorated +####################################### 其他小工具 ##################################### + +def get_reduce_token_percent(text): + """ + * 此函数未来将被弃用 + """ + try: + # text = "maximum context length is 4097 tokens. However, your messages resulted in 4870 tokens" + pattern = r"(\d+)\s+tokens\b" + match = re.findall(pattern, text) + EXCEED_ALLO = 500 # 稍微留一点余地,否则在回复时会因余量太少出问题 + max_limit = float(match[0]) - EXCEED_ALLO + current_tokens = float(match[1]) + ratio = max_limit/current_tokens + assert ratio > 0 and ratio < 1 + return ratio, str(int(current_tokens-max_limit)) + except: + return 0.5, '不详' + + + +def write_results_to_file(history, file_name=None): + """ + 将对话记录history以Markdown格式写入文件中。如果没有指定文件名,则使用当前时间生成文件名。 + """ + import os + import time + if file_name is None: + # file_name = time.strftime("chatGPT分析报告%Y-%m-%d-%H-%M-%S", time.localtime()) + '.md' + file_name = 'chatGPT分析报告' + \ + time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + '.md' + os.makedirs('./gpt_log/', exist_ok=True) + with open(f'./gpt_log/{file_name}', 'w', encoding='utf8') as f: + f.write('# chatGPT 分析报告\n') + for i, content in enumerate(history): + try: # 这个bug没找到触发条件,暂时先这样顶一下 + if type(content) != str: + content = str(content) + except: + continue + if i % 2 == 0: + f.write('## ') + f.write(content) + f.write('\n\n') + res = '以上材料已经被写入' + os.path.abspath(f'./gpt_log/{file_name}') + print(res) + return res + + +def regular_txt_to_markdown(text): + """ + 将普通文本转换为Markdown格式的文本。 + """ + text = text.replace('\n', '\n\n') + text = text.replace('\n\n\n', '\n\n') + text = text.replace('\n\n\n', '\n\n') + return text + + + + def report_execption(chatbot, history, a, b): """ - 向chatbot中添加错误信息 + 向chatbot中添加错误信息 """ chatbot.append((a, b)) history.append(a) @@ -225,7 +165,7 @@ def report_execption(chatbot, history, a, b): def text_divide_paragraph(text): """ - 将文本按照段落分隔符分割开,生成带有段落标签的HTML代码。 + 将文本按照段落分隔符分割开,生成带有段落标签的HTML代码。 """ if '```' in text: # careful input @@ -241,7 +181,7 @@ def text_divide_paragraph(text): def markdown_convertion(txt): """ - 将Markdown格式的文本转换为HTML格式。如果包含数学公式,则先将公式转换为HTML格式。 + 将Markdown格式的文本转换为HTML格式。如果包含数学公式,则先将公式转换为HTML格式。 """ pre = '
' suf = '
' @@ -333,7 +273,7 @@ def close_up_code_segment_during_stream(gpt_reply): def format_io(self, y): """ - 将输入和输出解析为HTML格式。将y中最后一项的输入部分段落化,并将输出部分的Markdown和数学公式转换为HTML格式。 + 将输入和输出解析为HTML格式。将y中最后一项的输入部分段落化,并将输出部分的Markdown和数学公式转换为HTML格式。 """ if y is None or y == []: return [] @@ -349,7 +289,7 @@ def format_io(self, y): def find_free_port(): """ - 返回当前系统中可用的未使用端口。 + 返回当前系统中可用的未使用端口。 """ import socket from contextlib import closing @@ -428,7 +368,7 @@ def find_recent_files(directory): return recent_files -def on_file_uploaded(files, chatbot, txt): +def on_file_uploaded(files, chatbot, txt, txt2, checkboxes): if len(files) == 0: return chatbot, txt import shutil @@ -450,13 +390,18 @@ def on_file_uploaded(files, chatbot, txt): dest_dir=f'private_upload/{time_tag}/{file_origin_name}.extract') moved_files = [fp for fp in glob.glob( 'private_upload/**/*', recursive=True)] - txt = f'private_upload/{time_tag}' + if "底部输入区" in checkboxes: + txt = "" + txt2 = f'private_upload/{time_tag}' + else: + txt = f'private_upload/{time_tag}' + txt2 = "" moved_files_str = '\t\n\n'.join(moved_files) chatbot.append(['我上传了文件,请查收', f'[Local Message] 收到以下文件: \n\n{moved_files_str}' + f'\n\n调用路径参数已自动修正到: \n\n{txt}' + f'\n\n现在您点击任意“红颜色”标识的函数插件时,以上文件将被作为输入参数'+err_msg]) - return chatbot, txt + return chatbot, txt, txt2 def on_report_generated(files, chatbot): @@ -469,9 +414,43 @@ def on_report_generated(files, chatbot): return report_files, chatbot def is_openai_api_key(key): - # 正确的 API_KEY 是 "sk-" + 48 位大小写字母数字的组合 API_MATCH = re.match(r"sk-[a-zA-Z0-9]{48}$", key) - return API_MATCH + return bool(API_MATCH) + +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(',') + for k in keys: + if is_any_api_key(k): return True + return False + else: + return is_openai_api_key(key) or is_api2d_key(key) + + +def select_api_key(keys, llm_model): + import random + avail_key_list = [] + key_list = keys.split(',') + + if llm_model.startswith('gpt-'): + for k in key_list: + if is_openai_api_key(k): avail_key_list.append(k) + + if llm_model.startswith('api2d-'): + for k in key_list: + if is_api2d_key(k): avail_key_list.append(k) + + if len(avail_key_list) == 0: + raise RuntimeError(f"您提供的api-key不满足要求,不包含任何可用于{llm_model}的api-key。") + + api_key = random.choice(avail_key_list) # 随机负载均衡 + return api_key @lru_cache(maxsize=128) def read_single_conf_with_lru_cache(arg): @@ -482,14 +461,13 @@ def read_single_conf_with_lru_cache(arg): r = getattr(importlib.import_module('config'), arg) # 在读取API_KEY时,检查一下是不是忘了改config if arg == 'API_KEY': - if is_openai_api_key(r): + if is_any_api_key(r): print亮绿(f"[API_KEY] 您的 API_KEY 是: {r[:15]}*** API_KEY 导入成功") else: - print亮红( "[API_KEY] 正确的 API_KEY 是 'sk-' + '48 位大小写字母数字' 的组合,请在config文件中修改API密钥, 添加海外代理之后再运行。" + \ - "(如果您刚更新过代码,请确保旧版config_private文件中没有遗留任何新增键值)") + print亮红( "[API_KEY] 正确的 API_KEY 是'sk'开头的51位密钥(OpenAI),或者 'fk'开头的41位密钥,请在config文件中修改API密钥之后再运行。") if arg == 'proxies': if r is None: - print亮红('[PROXY] 网络代理状态:未配置。无代理状态下很可能无法访问。建议:检查USE_PROXY选项是否修改。') + print亮红('[PROXY] 网络代理状态:未配置。无代理状态下很可能无法访问OpenAI家族的模型。建议:检查USE_PROXY选项是否修改。') else: print亮绿('[PROXY] 网络代理状态:已配置。配置信息如下:', r) assert isinstance(r, dict), 'proxies格式错误,请注意proxies选项的格式,不要遗漏括号。' diff --git a/version b/version index 962aee5..bb462e2 100644 --- a/version +++ b/version @@ -1,5 +1,5 @@ { - "version": 2.7, + "version": 3.1, "show_feature": true, - "new_feature": "修复BUG <-> 改善理解pdf(chatpdf)功能 <-> 如果一键更新失败,可前往github手动更新" + "new_feature": "添加支持清华ChatGLM和GPT-4 <-> 改进架构,支持与多个LLM模型同时对话 <-> 添加支持API2D(国内,可支持gpt4)<-> 支持多API-KEY负载均衡(并列填写,逗号分割) <-> 添加输入区文本清除按键" }