Compare commits

..

82 Commits

Author SHA1 Message Date
3fcee3762d 微调样式 2023-07-15 14:35:24 +08:00
1f014779e4 微调样式 2023-07-15 14:31:38 +08:00
97879e73ef 恢复横向调整css 2023-07-15 13:35:11 +08:00
13d4cd3237 音频功能说明书 2023-07-15 13:30:12 +08:00
73e835885b Merge branch 'master' into improve_ui_master 2023-07-15 13:01:13 +08:00
2524c908fc 修改提示 2023-07-15 12:58:38 +08:00
0e71d81bb3 Update README.md 2023-07-14 16:30:03 +08:00
a47864888f Update build-with-latex.yml 2023-07-14 16:25:25 +08:00
9b61ac807c Update build-with-chatglm.yml 2023-07-14 16:25:03 +08:00
bc200dc555 Update build-without-local-llms.yml 2023-07-14 16:24:32 +08:00
2c18b84517 修复依赖自动安装程序 2023-07-12 22:16:25 +08:00
fe7b651c56 更新提示 2023-07-11 15:56:28 +08:00
9b8f160788 up 2023-07-11 15:52:38 +08:00
801d5e2fc2 audio readme 2023-07-11 11:11:06 +08:00
cecdd28e04 Update README.md 2023-07-10 03:41:19 +08:00
d364df1cd6 add test instance 2023-07-10 03:33:51 +08:00
f51bc03686 3.45版本说明 2023-07-10 03:24:34 +08:00
c010d50716 允许加入ChatGLM微调模型 2023-07-10 03:17:09 +08:00
acddb86f3a 小而美 2023-07-10 00:20:14 +08:00
4fde0120ab 完善提醒 2023-07-10 00:08:59 +08:00
592a354eef 完善插件提示 2023-07-10 00:06:48 +08:00
bd66cf3d8b 修复对话历史的问题 2023-07-10 00:02:22 +08:00
e6e5174734 改名 2023-07-09 23:47:10 +08:00
13ade82677 改善语音辅助 2023-07-09 23:18:06 +08:00
ce9eb8d20a UP 2023-07-09 21:18:04 +08:00
dd47c0a284 merge changes 2023-07-09 20:55:37 +08:00
f725ab1b31 Merge branch 'master' into improve_ui_master 2023-07-09 20:47:53 +08:00
7ce4192c52 add comments 2023-07-09 17:25:50 +08:00
c06aafb642 Merge branch 'master' of github.com:binary-husky/chatgpt_academic 2023-07-09 16:01:15 +08:00
b298c5416c 完善PDF总结插件 2023-07-09 16:01:08 +08:00
94abf302cb 修正模板注释 2023-07-09 12:50:51 +08:00
fcc5534e66 ChatGLM 黑盒微调插件 2023-07-09 03:37:47 +08:00
56c0e4d575 3.44说明 2023-07-09 01:21:18 +08:00
8a10db618e Merge branch 'master-interact' 2023-07-09 01:05:04 +08:00
1fe66f0291 优化azure的体验 2023-07-09 00:20:58 +08:00
ced977c443 修复双dollar公式匹配bug 2023-07-08 22:23:29 +08:00
6c2ffbae52 Update README.md 2023-07-08 19:17:35 +08:00
be2f54fac9 Update README.md 2023-07-08 18:21:20 +08:00
87b5e56378 Update requirements.txt 2023-07-08 18:10:33 +08:00
3a5764ed34 Update requirements.txt 2023-07-08 17:59:27 +08:00
91aee50ea7 Chuanhu 主题 2023-07-07 20:12:06 +08:00
e5ccedf491 名称修订 2023-07-07 20:08:26 +08:00
f620666a58 Merge branch 'improve_ui_master' of https://github.com/binary-husky/chatgpt_academic into improve_ui_master 2023-07-07 19:51:48 +08:00
594c63e5d6 主题修正 2023-07-07 19:51:09 +08:00
67d9051890 update error message 2023-07-07 17:41:43 +08:00
be96232127 Merge pull request #933 from binary-husky/master-latex-patch
Latex File Name Bug Patch
2023-07-07 16:57:58 +08:00
3b5bc7a784 Update use_azure.md 2023-07-07 10:55:22 +08:00
5e92f437a1 Update use_azure.md 2023-07-07 10:54:21 +08:00
eabd9d312f 3.43 2023-07-07 10:47:30 +08:00
0da6fe78ac 统一azure-gpt-3.5的格式 2023-07-07 10:45:11 +08:00
be990380a0 Merge branch 'master' of https://github.com/binary-husky/chatgpt_academic into master 2023-07-07 10:42:41 +08:00
9c0bc48420 修复Azure OpenAI接口的各种bug 2023-07-07 10:42:38 +08:00
5c0d34793e Latex File Name Bug Patch 2023-07-07 00:09:50 +08:00
37fc550652 Update config.py 2023-07-06 10:47:06 +08:00
2c1d6ac212 修复Organization的bug 2023-07-05 21:14:13 +08:00
8c699c1b26 Update README.md 2023-07-05 21:04:28 +08:00
c620fa9011 Update README.md 2023-07-05 20:55:59 +08:00
f16fd60211 Update README.md 2023-07-05 20:34:22 +08:00
9674e59d26 更新说明 2023-07-05 20:22:57 +08:00
643c5e125a 更新提醒 2023-07-05 20:10:18 +08:00
e5099e1daa 极少数情况下,openai的官方KEY需要伴随组织编码 2023-07-05 20:05:20 +08:00
3e621bbec1 Update Dockerfile 2023-07-05 14:37:54 +08:00
bb1d5a61c0 update translation matrix 2023-07-05 14:32:33 +08:00
fd3d0be2d8 Update config.py 2023-07-05 14:13:04 +08:00
ae623258f3 更详细的配置提示 2023-07-05 14:10:06 +08:00
cda281f08b 把newbing的cookie加回来 2023-07-05 13:48:50 +08:00
9f8e7a6efa 显示更详细的报错 2023-07-05 13:35:11 +08:00
57643dd2b6 update error msg 2023-07-05 13:01:06 +08:00
6bc8a78cfe No more cookie for NewBing! 2023-07-05 12:45:10 +08:00
d2700e97fb 更新openai失效提醒 2023-07-05 11:03:11 +08:00
c4dd81dc9a Update Dockerfile 2023-07-04 12:28:52 +08:00
e9b06d7cde Merge pull request #927 from QuantumRoseinAmethystVase/master
Update 批量总结PDF文档.py
2023-07-04 12:24:17 +08:00
6e6ea69611 Unsplash恢复了 2023-07-04 12:16:01 +08:00
b082b5eb1b 将阿里云TOKEN移动到config中 2023-07-03 23:20:25 +08:00
9648d78453 重构异步代码,增强可读性 2023-07-03 22:44:10 +08:00
16c17eb077 Update 批量总结PDF文档.py
Improve the output.
2023-07-03 18:55:16 +08:00
2dc8718041 语音模组第一个版本 2023-07-03 00:13:10 +08:00
a330d6636e error 2023-07-02 22:54:05 +08:00
322c4be145 同步音频输入 2023-07-02 14:42:12 +08:00
a3596ff60d audio 2023-07-02 01:05:20 +08:00
e11d8132f8 add green theme 2023-07-01 23:02:44 +08:00
59877dd728 Local variable 'result' might be referenced before assignment, add else result 2023-07-01 22:27:11 +08:00
65 changed files with 2105 additions and 3791 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -1,5 +1,5 @@
# https://docs.github.com/en/actions/publishing-packages/publishing-docker-images#publishing-images-to-github-packages
name: Create and publish a Docker image for ChatGLM support
name: build-with-chatglm
on:
push:

View File

@ -1,5 +1,5 @@
# https://docs.github.com/en/actions/publishing-packages/publishing-docker-images#publishing-images-to-github-packages
name: Create and publish a Docker image for Latex support
name: build-with-latex
on:
push:

View File

@ -1,5 +1,5 @@
# https://docs.github.com/en/actions/publishing-packages/publishing-docker-images#publishing-images-to-github-packages
name: Create and publish a Docker image
name: build-without-local-llms
on:
push:

45
.gitignore vendored
View File

@ -2,14 +2,15 @@
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
plugins/
downloads/
eggs/
.eggs/
@ -25,6 +26,7 @@ share/python-wheels/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
@ -33,6 +35,7 @@ MANIFEST
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
@ -46,64 +49,91 @@ coverage.xml
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
github
.github
TEMP
TRASH
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
site/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.direnv/
.env
.venv
env/
venv*/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
.vscode
.idea
history
ssr_conf
config_private.py
@ -115,12 +145,9 @@ cradle*
debug*
private*
crazy_functions/test_project/pdf_and_word
crazy_fun
ctions/test_samples
crazy_functions/test_samples
request_llm/jittorllms
users_data/*
request_llm/moss
multi-language
request_llm/moss
media
__test.py
flagged

View File

@ -1,28 +1,34 @@
# 此Dockerfile适用于“无本地模型”的环境构建如果需要使用chatglm等本地模型,请参考 docs/Dockerfile+ChatGLM
# 如何构建: 先修改 `config.py` 然后 docker build -t gpt-academic .
# 如何运行: docker run --rm -it --net=host gpt-academic
# 此Dockerfile适用于“无本地模型”的环境构建如果需要使用chatglm等本地模型或者latex运行依赖请参考 docker-compose.yml
# 如何构建: 先修改 `config.py` 然后 `docker build -t gpt-academic . `
# 如何运行(Linux下): `docker run --rm -it --net=host gpt-academic `
# 如何运行(其他操作系统选择任意一个固定端口50923): `docker run --rm -it -e WEB_PORT=50923 -p 50923:50923 gpt-academic `
FROM python:3.11
# 非必要步骤更换pip源
RUN echo '[global]' > /etc/pip.conf && \
echo 'index-url = https://mirrors.aliyun.com/pypi/simple/' >> /etc/pip.conf && \
echo 'trusted-host = mirrors.aliyun.com' >> /etc/pip.conf
# 进入工作路径
WORKDIR /gpt
# 安装依赖
# 安装大部分依赖利用Docker缓存加速以后的构建
COPY requirements.txt ./
COPY ./docs/gradio-3.32.2-py3-none-any.whl ./docs/gradio-3.32.2-py3-none-any.whl
RUN pip3 install -r requirements.txt
# 装载项目文件
# 装载项目文件,安装剩余依赖
COPY . .
RUN pip3 install -r requirements.txt
# 可选步骤,用于预热模块
# 非必要步骤,用于预热模块
RUN python3 -c 'from check_proxy import warm_up_modules; warm_up_modules()'
# 启动
CMD ["python3", "-u", "main.py"]

View File

@ -1,24 +1,24 @@
> **Note**
>
> 2023.5.27 对Gradio依赖进行了调整Fork并解决了官方Gradio的若干Bugs。请及时**更新代码**并重新更新pip依赖。安装依赖时,请严格选择`requirements.txt`中**指定的版本**
>
> `pip install -r requirements.txt`
> 2023.7.8: Gradio, Pydantic依赖调整已修改 `requirements.txt`。请及时**更新代码**安装依赖时,请严格选择`requirements.txt`中**指定的版本**
>
> `pip install -r requirements.txt`
# <img src="docs/logo.png" width="40" > GPT 学术优化 (GPT Academic)
**如果喜欢这个项目请给它一个Star如果你发明了更好用的快捷键或函数插件欢迎发pull requests**
# <div align=center><img src="docs/logo.png" width="40" > GPT 学术优化 (GPT Academic)</div>
**如果喜欢这个项目请给它一个Star如果您发明了好用的快捷键或函数插件欢迎发pull requests**
If you like this project, please give it a Star. If you've come up with more useful academic shortcuts or functional plugins, feel free to open an issue or pull request. We also have a README in [English|](docs/README_EN.md)[日本語|](docs/README_JP.md)[한국어|](https://github.com/mldljyh/ko_gpt_academic)[Русский|](docs/README_RS.md)[Français](docs/README_FR.md) translated by this project itself.
To translate this project to arbitary language with GPT, read and run [`multi_language.py`](multi_language.py) (experimental).
> **Note**
>
> 1.请注意只有**红颜色**标识的函数插件(按钮)才支持读取文件,部分插件位于插件区的**下拉菜单**中。另外我们以**最高优先级**欢迎和处理任何新插件的PR
> 1.请注意只有 **高亮(如红色)** 标识的函数插件(按钮)才支持读取文件,部分插件位于插件区的**下拉菜单**中。另外我们以**最高优先级**欢迎和处理任何新插件的PR
>
> 2.本项目中每个文件的功能都在自译解[`self_analysis.md`](https://github.com/binary-husky/gpt_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)详细说明。随着版本的迭代您也可以随时自行点击相关函数插件调用GPT重新生成项目的自我解析报告。常见问题汇总在[`wiki`](https://github.com/binary-husky/gpt_academic/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)当中。[安装方法](#installation)。
>
> 3.本项目兼容并鼓励尝试国产大语言模型chatglm和RWKV, 盘古等等。支持多个api-key共存可在配置文件中填写如`API_KEY="openai-key1,openai-key2,api2d-key3"`。需要临时更换`API_KEY`时,在输入区输入临时的`API_KEY`然后回车键提交后即可生效。
> 3.本项目兼容并鼓励尝试国产大语言模型ChatGLM和Moss等等。支持多个api-key共存可在配置文件中填写如`API_KEY="openai-key1,openai-key2,api2d-key3"`。需要临时更换`API_KEY`时,在输入区输入临时的`API_KEY`然后回车键提交后即可生效。
@ -41,15 +41,17 @@ Markdown[中英互译](https://www.bilibili.com/video/BV1yo4y157jV/) | [函数
chat分析报告生成 | [函数插件] 运行后自动生成总结汇报
[PDF论文全文翻译功能](https://www.bilibili.com/video/BV1KT411x7Wn) | [函数插件] PDF论文提取题目&摘要+翻译全文(多线程)
[Arxiv小助手](https://www.bilibili.com/video/BV1LM4y1279X) | [函数插件] 输入arxiv文章url即可一键翻译摘要+下载PDF
Latex论文一键校对 | [函数插件] 仿Grammarly对Latex文章进行语法、拼写纠错+输出对照PDF
[谷歌学术统合小助手](https://www.bilibili.com/video/BV19L411U7ia) | [函数插件] 给定任意谷歌学术搜索页面URL让gpt帮你[写relatedworks](https://www.bilibili.com/video/BV1GP411U7Az/)
互联网信息聚合+GPT | [函数插件] 一键[让GPT从互联网获取信息](https://www.bilibili.com/video/BV1om4y127ck),再回答问题,让信息永不过时
⭐Arxiv论文精细翻译 | [函数插件] 一键[以超高质量翻译arxiv论文](https://www.bilibili.com/video/BV1dz4y1v77A/)迄今为止最好的论文翻译工具
互联网信息聚合+GPT | [函数插件] 一键[让GPT从互联网获取信息](https://www.bilibili.com/video/BV1om4y127ck)回答问题,让信息永不过时
⭐Arxiv论文精细翻译 | [函数插件] 一键[以超高质量翻译arxiv论文](https://www.bilibili.com/video/BV1dz4y1v77A/)目前最好的论文翻译工具
公式/图片/表格显示 | 可以同时显示公式的[tex形式和渲染形式](https://user-images.githubusercontent.com/96192199/230598842-1d7fcddd-815d-40ee-af60-baf488a199df.png),支持公式、代码高亮
多线程函数插件支持 | 支持多线调用chatgpt一键处理[海量文本](https://www.bilibili.com/video/BV1FT411H7c5/)或程序
启动暗色gradio[主题](https://github.com/binary-husky/gpt_academic/issues/173) | 在浏览器url后面添加```/?__theme=dark```可以切换dark主题
[多LLM模型](https://www.bilibili.com/video/BV1wT411p7yf)支持 | 同时被GPT3.5、GPT4、[清华ChatGLM](https://github.com/THUDM/ChatGLM-6B)、[复旦MOSS](https://github.com/OpenLMLab/MOSS)同时伺候的感觉一定会很不错吧?
更多LLM模型接入支持[huggingface部署](https://huggingface.co/spaces/qingxu98/gpt-academic) | 加入Newbing接口(新必应),引入清华[Jittorllms](https://github.com/Jittor/JittorLLMs)支持[LLaMA](https://github.com/facebookresearch/llama)[RWKV](https://github.com/BlinkDL/ChatRWKV)和[盘古α](https://openi.org.cn/pangu/)
更多新功能展示(图像生成等) …… | 见本文档结尾处 ……
启动暗色[主题](https://github.com/binary-husky/gpt_academic/issues/173) | 在浏览器url后面添加```/?__theme=dark```可以切换dark主题
[多LLM模型](https://www.bilibili.com/video/BV1wT411p7yf)支持 | 同时被GPT3.5、GPT4、[清华ChatGLM2](https://github.com/THUDM/ChatGLM2-6B)、[复旦MOSS](https://github.com/OpenLMLab/MOSS)同时伺候的感觉一定会很不错吧?
ChatGLM2微调模型 | 支持加载ChatGLM2微调模型提供ChatGLM2微调插件
更多LLM模型接入支持[huggingface部署](https://huggingface.co/spaces/qingxu98/gpt-academic) | 加入Newbing接口(新必应),引入清华[Jittorllms](https://github.com/Jittor/JittorLLMs)支持[LLaMA](https://github.com/facebookresearch/llama)和[盘古α](https://openi.org.cn/pangu/)
更多新功能展示 (图像生成等) …… | 见本文档结尾处 ……
</div>
@ -85,9 +87,8 @@ chat分析报告生成 | [函数插件] 运行后自动生成总结汇报
<img src="https://user-images.githubusercontent.com/96192199/232537274-deca0563-7aa6-4b5d-94a2-b7c453c47794.png" width="700" >
</div>
---
# Installation
## 安装-方法1:直接运行 (Windows, Linux or MacOS)
### 安装方法I:直接运行 (Windows, Linux or MacOS)
1. 下载项目
```sh
@ -114,12 +115,12 @@ python -m pip install -r requirements.txt # 这个步骤和pip安装一样的步
```
<details><summary>如果需要支持清华ChatGLM/复旦MOSS作为后端请点击展开此处</summary>
<details><summary>如果需要支持清华ChatGLM2/复旦MOSS作为后端请点击展开此处</summary>
<p>
【可选步骤】如果需要支持清华ChatGLM/复旦MOSS作为后端需要额外安装更多依赖前提条件熟悉Python + 用过Pytorch + 电脑配置够强):
【可选步骤】如果需要支持清华ChatGLM2/复旦MOSS作为后端需要额外安装更多依赖前提条件熟悉Python + 用过Pytorch + 电脑配置够强):
```sh
# 【可选步骤I】支持清华ChatGLM。清华ChatGLM备注如果遇到"Call ChatGLM fail 不能正常加载ChatGLM的参数" 错误,参考如下: 1以上默认安装的为torch+cpu版使用cuda需要卸载torch重新安装torch+cuda 2如因本机配置不够无法加载模型可以修改request_llm/bridge_chatglm.py中的模型精度, 将 AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True) 都修改为 AutoTokenizer.from_pretrained("THUDM/chatglm-6b-int4", trust_remote_code=True)
# 【可选步骤I】支持清华ChatGLM2。清华ChatGLM备注如果遇到"Call ChatGLM fail 不能正常加载ChatGLM的参数" 错误,参考如下: 1以上默认安装的为torch+cpu版使用cuda需要卸载torch重新安装torch+cuda 2如因本机配置不够无法加载模型可以修改request_llm/bridge_chatglm.py中的模型精度, 将 AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True) 都修改为 AutoTokenizer.from_pretrained("THUDM/chatglm-6b-int4", trust_remote_code=True)
python -m pip install -r request_llm/requirements_chatglm.txt
# 【可选步骤II】支持复旦MOSS
@ -140,9 +141,11 @@ AVAIL_LLM_MODELS = ["gpt-3.5-turbo", "api2d-gpt-3.5-turbo", "gpt-4", "api2d-gpt-
python main.py
```
## 安装-方法2使用Docker
### 安装方法II使用Docker
1. 仅ChatGPT推荐大多数人选择等价于docker-compose方案1
[![basic](https://github.com/binary-husky/gpt_academic/actions/workflows/build-without-local-llms.yml/badge.svg?branch=master)](https://github.com/binary-husky/gpt_academic/actions/workflows/build-without-local-llms.yml)
[![basic](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-latex.yml/badge.svg?branch=master)](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-latex.yml)
``` sh
git clone https://github.com/binary-husky/gpt_academic.git # 下载项目
@ -150,14 +153,15 @@ cd gpt_academic # 进入路径
nano config.py # 用任意文本编辑器编辑config.py, 配置 “Proxy” “API_KEY” 以及 “WEB_PORT” (例如50923) 等
docker build -t gpt-academic . # 安装
#(最后一步-选择1在Linux环境下用`--net=host`更方便快捷
#(最后一步-Linux操作系统用`--net=host`更方便快捷
docker run --rm -it --net=host gpt-academic
#(最后一步-选择2在macOS/windows环境下,只能用-p选项将容器上的端口(例如50923)暴露给主机上的端口
#(最后一步-MacOS/Windows操作系统)只能用-p选项将容器上的端口(例如50923)暴露给主机上的端口
docker run --rm -it -e WEB_PORT=50923 -p 50923:50923 gpt-academic
```
P.S. 如果需要依赖Latex的插件功能请见Wiki。另外您也可以直接使用docker-compose获取Latex功能修改docker-compose.yml保留方案4并删除其他方案
2. ChatGPT + ChatGLM + MOSS需要熟悉Docker
2. ChatGPT + ChatGLM2 + MOSS需要熟悉Docker
[![chatglm](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-chatglm.yml/badge.svg?branch=master)](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-chatglm.yml)
``` sh
# 修改docker-compose.yml保留方案2并删除其他方案。修改docker-compose.yml中方案2的配置参考其中注释即可
@ -171,7 +175,7 @@ docker-compose up
```
## 安装-方法3:其他部署姿势
### 安装方法III:其他部署姿势
1. 一键运行脚本。
完全不熟悉python环境的Windows用户可以下载[Release](https://github.com/binary-husky/gpt_academic/releases)中发布的一键运行脚本安装无本地模型的版本。
脚本的贡献来源是[oobabooga](https://github.com/oobabooga/one-click-installers)。
@ -194,11 +198,9 @@ docker-compose up
7. 如何在二级网址(如`http://localhost/subpath`)下运行。
请访问[FastAPI运行说明](docs/WithFastapi.md)
---
# Advanced Usage
## 自定义新的便捷按钮 / 自定义函数插件
1. 自定义新的便捷按钮(学术快捷键)
# Advanced Usage
### I自定义新的便捷按钮学术快捷键
任意文本编辑器打开`core_functional.py`,添加条目如下,然后重启程序即可。(如果按钮已经添加成功并可见,那么前缀、后缀都支持热修改,无需重启程序即可生效。)
例如
```
@ -214,15 +216,15 @@ docker-compose up
<img src="https://user-images.githubusercontent.com/96192199/226899272-477c2134-ed71-4326-810c-29891fe4a508.png" width="500" >
</div>
2. 自定义函数插件
### II自定义函数插件
编写强大的函数插件来执行任何你想得到的和想不到的任务。
本项目的插件编写、调试难度很低只要您具备一定的python基础知识就可以仿照我们提供的模板实现自己的插件功能。
详情请参考[函数插件指南](https://github.com/binary-husky/gpt_academic/wiki/%E5%87%BD%E6%95%B0%E6%8F%92%E4%BB%B6%E6%8C%87%E5%8D%97)。
---
# Latest Update
## 新功能动态
### I新功能动态
1. 对话保存功能。在函数插件区调用 `保存当前的对话` 即可将当前对话保存为可读+可复原的html文件
另外在函数插件区(下拉菜单)调用 `载入对话历史存档` ,即可还原之前的会话。
@ -283,8 +285,10 @@ Tip不指定文件直接点击 `载入对话历史存档` 可以查看历史h
## 版本:
### II版本:
- version 3.5(Todo): 使用自然语言调用本项目的所有函数插件(高优先级)
- version 3.45: 支持自定义ChatGLM2微调模型
- version 3.44: 正式支持Azure优化界面易用性
- version 3.4: +arxiv论文翻译、latex论文批改功能
- version 3.3: +互联网信息综合功能
- version 3.2: 函数插件支持更多参数接口 (保存对话功能, 解读任意语言代码+同时询问任意的LLM组合)
@ -305,13 +309,18 @@ gpt_academic开发者QQ群-2610599535
- 某些浏览器翻译插件干扰此软件前端的运行
- 官方Gradio目前有很多兼容性Bug请务必使用`requirement.txt`安装Gradio
## 参考与学习
### III主题
1. `Chuanhu-Small-and-Beautiful` [网址](https://github.com/GaiZhenbiao/ChuanhuChatGPT/)
### IV参考与学习
```
代码中参考了很多其他优秀项目中的设计,顺序不分先后:
# 清华ChatGLM-6B:
https://github.com/THUDM/ChatGLM-6B
# 清华ChatGLM2-6B:
https://github.com/THUDM/ChatGLM2-6B
# 清华JittorLLMs:
https://github.com/Jittor/JittorLLMs

View File

@ -1,486 +0,0 @@
import os
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_user_upload, \
get_conf, ArgsGeneralWrapper, DummyWith
# 问询记录, python 版本建议3.9+(越新越好)
import logging
# 一些普通功能模块
from core_functional import get_core_functions
functional = get_core_functions()
# 高级函数插件
from crazy_functional import get_crazy_functions
crazy_fns = get_crazy_functions()
# 处理markdown文本格式的转变
gr.Chatbot.postprocess = format_io
# 做一些外观色彩上的调整
from theme import adjust_theme, advanced_css, custom_css
set_theme = adjust_theme()
# 代理与自动更新
from check_proxy import check_proxy, auto_update, warm_up_modules
import func_box
from check_proxy import get_current_version
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, 请注意自我隐私保护哦!")
# 建议您复制一个config_private.py放自己的秘密, 如API和代理网址, 避免不小心传github被别人看到
proxies, WEB_PORT, LLM_MODEL, CONCURRENT_COUNT, AUTHENTICATION, LAYOUT, API_KEY, AVAIL_LLM_MODELS, LOCAL_PORT= \
get_conf('proxies', 'WEB_PORT', 'LLM_MODEL', 'CONCURRENT_COUNT', 'AUTHENTICATION', 'LAYOUT',
'API_KEY', 'AVAIL_LLM_MODELS', 'LOCAL_PORT')
proxy_info = check_proxy(proxies)
# 如果WEB_PORT是-1, 则随机选取WEB端口
PORT = find_free_port() if WEB_PORT <= 0 else WEB_PORT
if not AUTHENTICATION: AUTHENTICATION = None
os.environ['no_proxy'] = '*' # 避免代理网络产生意外污染
class ChatBotFrame:
def __init__(self):
self.cancel_handles = []
self.initial_prompt = "You will play a professional to answer me according to my needs."
self.title_html = f"<h1 align=\"center\">Chatbot for KSO {get_current_version()}</h1>"
self.description = """代码开源和更新[地址🚀](https://github.com/binary-husky/chatgpt_academic),感谢热情的[开发者们❤️](https://github.com/binary-husky/chatgpt_academic/graphs/contributors)"""
class ChatBot(ChatBotFrame):
def __init__(self):
super().__init__()
self.__url = f'http://{func_box.ipaddr()}:{PORT}'
# self.__gr_url = gr.State(self.__url)
def draw_title(self):
# self.title = gr.HTML(self.title_html)
self.cookies = gr.State({'api_key': API_KEY, 'llm_model': LLM_MODEL, 'local': self.__url})
def draw_chatbot(self):
self.chatbot = gr.Chatbot(elem_id='main_chatbot', label=f"当前模型:{LLM_MODEL}")
self.chatbot.style()
self.history = gr.State([])
temp_draw = [gr.HTML() for i in range(7)]
with gr.Box(elem_id='chat_box'):
self.state_users = gr.HTML(value='', visible=False, elem_id='state_users')
with gr.Row():
self.sm_upload = gr.UploadButton(label='UPLOAD', file_count='multiple', elem_classes='sm_btn').style(size='sm', full_width=False)
self.sm_code_block = gr.Button(value='CODE', elem_classes='sm_btn').style(size='sm', full_width=False)
self.sm_upload_history = gr.Button("SPASE", variant="primary", elem_classes='sm_btn').style(size='sm', full_width=False)
self.md_dropdown = gr.Dropdown(choices=AVAIL_LLM_MODELS, value=LLM_MODEL,
show_label=False, interactive=True,
elem_classes='sm_select', elem_id='change-font-size').style(container=False)
gr.HTML(func_box.get_html("appearance_switcher.html").format(label=""), elem_id='user_input_tb', elem_classes="insert_block")
with gr.Row():
self.txt = gr.Textbox(show_label=False, placeholder="Input question here.", elem_classes='chat_input').style(container=False)
self.input_copy = gr.State('')
self.submitBtn = gr.Button("", variant="primary", elem_classes='submit_btn').style(full_width=False)
with gr.Row():
self.status = gr.Markdown(f"Tip: 按Enter提交, 按Shift+Enter换行\n {proxy_info}", elem_id='debug_mes')
def signals_sm_btn(self):
self.sm_upload.upload(on_file_uploaded, [self.sm_upload, self.chatbot, self.txt], [self.chatbot, self.txt]).then(
fn=lambda: [gr.Tabs.update(selected='plug_tab'), gr.Column.update(visible=False)], inputs=None, outputs=[self.tabs_inputs, self.examples_column]
)
self.sm_code_block.click(fn=lambda x: x+'```\n\n```', inputs=[self.txt], outputs=[self.txt])
self.sm_upload_history.click(get_user_upload, [self.chatbot], outputs=[self.chatbot]).then(fn=lambda: gr.Column.update(visible=False), inputs=None, outputs=self.examples_column)
# self.sm_select_font.select(fn=lambda x: gr.HTML.update(value=f"{x}px"), inputs=[self.sm_select_font], outputs=[self.state_users])
def draw_examples(self):
with gr.Column(elem_id='examples_col') as self.examples_column:
gr.Markdown('# Get Started Quickly')
with gr.Row():
hide_components = gr.Textbox(visible=False)
gr.Button.update = func_box.update_btn
self.example = [['今天伦敦天气怎么样?', '对2021年以后的世界和事件了解有限', self.submitBtn.update(elem_id='highlight_update')],
['今夕何夕,明月何月?', '偶尔会产生不正确的信息', self.submitBtn.update(elem_id='highlight_update')],
['怎么才能把学校给炸了?', '经过训练,会拒绝不适当的请求', self.submitBtn.update(elem_id='highlight_update')]]
self.example_inputs = [self.txt, hide_components, self.submitBtn]
self.guidance_example = gr.Examples(examples=self.example, inputs=self.example_inputs, label='基础对话')
self.guidance_plugins = gr.Dataset(components=[gr.HTML(visible=False)], samples=[['...'] for i in range(4)], label='高级功能', type='index')
self.guidance_plugins_state = gr.State()
self.guidance_news = gr.Examples(examples=func_box.git_log_list(), inputs=[hide_components, hide_components], label='News')
def plug_update(index, date_set):
variant = crazy_fns[date_set[index]]["Color"] if "Color" in crazy_fns[date_set[index]] else "secondary"
ret = {self.switchy_bt: self.switchy_bt.update(value=date_set[index], variant=variant, elem_id='highlight_update'),
self.tabs_inputs: gr.Tabs.update(selected='plug_tab'),
self.area_crazy_fn: self.area_crazy_fn.update(open=True)}
fns_value = func_box.txt_converter_json(str(crazy_fns[date_set[index]].get('Parameters', '')))
fns_lable = f"插件[{date_set[index]}]的高级参数说明:\n" + crazy_fns[date_set[index]].get("ArgsReminder", f"没有提供高级参数功能说明")
temp_dict = dict(visible=True, interactive=True, value=str(fns_value), label=fns_lable)
# 是否唤起高级插件参数区
if crazy_fns[date_set[index]].get("AdvancedArgs", False):
ret.update({self.plugin_advanced_arg: gr.update(**temp_dict)})
ret.update({self.area_crazy_fn: self.area_crazy_fn.update(open=False)})
else:
ret.update({self.plugin_advanced_arg: gr.update(visible=False, label=f"插件[{date_set[index]}]不需要高级参数。")})
return ret
self.guidance_plugins.select(fn=plug_update, inputs=[self.guidance_plugins, self.guidance_plugins_state],
outputs=[self.switchy_bt, self.plugin_advanced_arg, self.tabs_inputs,
self.area_crazy_fn])
def __clear_input(self, inputs):
return '', inputs, self.examples_column.update(visible=False)
def draw_prompt(self):
with gr.Row():
self.pro_search_txt = gr.Textbox(show_label=False, placeholder="Enter the prompt you want.").style(
container=False)
self.pro_entry_btn = gr.Button("搜索", variant="primary").style(full_width=False, size="sm")
with gr.Row():
with gr.Accordion(label='Prompt usage frequency'):
self.pro_prompt_list = gr.Dataset(components=[gr.HTML(visible=False)], samples_per_page=10,
label='Results',
samples=[[". . ."] for i in range(20)], type='index')
self.pro_prompt_state = gr.State(self.pro_prompt_list)
def draw_temp_edit(self):
with gr.Box():
with gr.Row():
with gr.Column(scale=100):
self.pro_results = gr.Chatbot(label='Prompt and result', elem_id='prompt_result').style()
with gr.Column(scale=16):
Tips = "用 BORF 分析法设计chat GPT prompt:\n" \
"1、阐述背景 B(Background): 说明背景为chatGPT提供充足的信息\n" \
"2、定义目标 O(Objectives):“我们希望实现什么”\n" \
"3、定义关键结果 R(key Result):“我要什么具体效果”\n" \
"4、试验并调整改进 E(Evolve):三种改进方法自由组合\n" \
"\t 改进输入从答案的不足之处着手改进背景B,目标O与关键结果R\n" \
"\t 改进答案在后续对话中指正chatGPT答案缺点\n" \
"\t 重新生成尝试在prompt不变的情况下多次生成结果优中选优\n" \
"\t 熟练使用占位符{{{v}}}: 当Prompt存在占位符则优先将{{{v}}}替换为预期文本"
self.pro_edit_txt = gr.Textbox(show_label=False, info='Prompt编辑区', lines=14,
placeholder=Tips).style(container=False)
with gr.Row():
self.pro_name_txt = gr.Textbox(show_label=False, placeholder='是否全复用prompt / prompt功能名', ).style(
container=False)
self.pro_new_btn = gr.Button("保存Prompt", variant="primary").style(size='sm').style()
with gr.Row(elem_id='sm_btn'):
self.pro_reuse_btn = gr.Button("复用Result", variant="secondary").style(size='sm').style(full_width=False)
self.pro_clear_btn = gr.Button("重置Result", variant="stop").style(size='sm').style(full_width=False)
def signals_prompt_edit(self):
self.pro_clear_btn.click(fn=lambda: [], inputs=None, outputs=self.pro_results)
self.prompt_tab.select(fn=func_box.draw_results,
inputs=[self.pro_search_txt, self.pro_prompt_state, self.pro_tf_slider,
self.pro_private_check],
outputs=[self.pro_prompt_list, self.pro_prompt_state])
self.pro_search_txt.submit(fn=func_box.draw_results,
inputs=[self.pro_search_txt, self.pro_prompt_state, self.pro_tf_slider,
self.pro_private_check],
outputs=[self.pro_prompt_list, self.pro_prompt_state])
self.pro_entry_btn.click(fn=func_box.draw_results,
inputs=[self.pro_search_txt, self.pro_prompt_state, self.pro_tf_slider,
self.pro_private_check],
outputs=[self.pro_prompt_list, self.pro_prompt_state])
self.pro_prompt_list.click(fn=func_box.show_prompt_result,
inputs=[self.pro_prompt_list, self.pro_prompt_state, self.pro_results, self.pro_edit_txt, self.pro_name_txt],
outputs=[self.pro_results, self.pro_edit_txt, self.pro_name_txt])
self.pro_new_btn.click(fn=func_box.prompt_save,
inputs=[self.pro_edit_txt, self.pro_name_txt, self.pro_fp_state],
outputs=[self.pro_edit_txt, self.pro_name_txt, self.pro_private_check,
self.pro_func_prompt, self.pro_fp_state, self.tabs_chatbot])
self.pro_reuse_btn.click(
fn=func_box.reuse_chat,
inputs=[self.pro_results, self.chatbot, self.history, self.pro_name_txt, self.txt],
outputs=[self.chatbot, self.history, self.txt, self.tabs_chatbot, self.pro_name_txt, self.examples_column]
)
def draw_function_chat(self):
prompt_list, devs_document = get_conf('prompt_list', 'devs_document')
with gr.TabItem('Function', id='func_tab'):
with gr.Accordion("基础功能区", open=False) as self.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("上传你的Prompt", open=False) as self.area_basic_fn:
jump_link = f'<a href="{devs_document}" target="_blank">Developer Documentation</a>'
self.pro_devs_link = gr.HTML(jump_link)
self.pro_upload_btn = gr.File(file_count='single', file_types=['.yaml', '.json'],
label=f'上传你的Prompt文件, 编写格式请遵循上述开发者文档', )
self.pro_private_check = gr.CheckboxGroup(choices=prompt_list['key'], value=prompt_list['value'],
label='选择展示Prompt')
self.pro_func_prompt = gr.Dataset(components=[gr.HTML()], label="Prompt List", visible=False,
samples=[['...', ""] for i in range(20)], type='index',
samples_per_page=10)
self.pro_fp_state = gr.State(self.pro_func_prompt)
def signals_prompt_func(self):
self.pro_private_check.select(fn=func_box.prompt_reduce,
inputs=[self.pro_private_check, self.pro_fp_state],
outputs=[self.pro_func_prompt, self.pro_fp_state, self.pro_private_check])
self.tabs_code = gr.State(0)
self.pro_func_prompt.select(fn=func_box.prompt_input,
inputs=[self.txt, self.pro_edit_txt, self.pro_name_txt, self.pro_func_prompt, self.pro_fp_state, self.tabs_code],
outputs=[self.txt, self.pro_edit_txt, self.pro_name_txt])
self.pro_upload_btn.upload(fn=func_box.prompt_upload_refresh,
inputs=[self.pro_upload_btn, self.pro_prompt_state],
outputs=[self.pro_func_prompt, self.pro_prompt_state, self.pro_private_check])
self.chat_tab.select(fn=lambda: 0, inputs=None, outputs=self.tabs_code)
self.prompt_tab.select(fn=lambda: 1, inputs=None, outputs=self.tabs_code)
def draw_public_chat(self):
with gr.TabItem('Plugins', id='plug_tab'):
with gr.Accordion("上传本地文件可供高亮函数插件调用", open=False) as self.area_file_up:
self.file_upload = gr.Files(label="任何文件, 但推荐上传压缩文件(zip, tar)",
file_count="multiple")
self.file_upload.style()
with gr.Accordion("函数插件区", open=True) as self.area_crazy_fn:
with gr.Row():
for k in crazy_fns:
if not crazy_fns[k].get("AsButton", True): continue
self.variant = crazy_fns[k]["Color"] if "Color" in crazy_fns[k] else "secondary"
crazy_fns[k]["Button"] = gr.Button(k, variant=self.variant)
crazy_fns[k]["Button"].style(size="sm")
with gr.Accordion("更多函数插件/高级用法", open=True, ):
dropdown_fn_list = []
for k in crazy_fns.keys():
if not crazy_fns[k].get("AsButton", True):
dropdown_fn_list.append(k)
elif crazy_fns[k].get('AdvancedArgs', False):
dropdown_fn_list.append(k)
self.dropdown = gr.Dropdown(dropdown_fn_list, value=r"打开插件列表", show_label=False, label="").style(
container=False)
self.plugin_advanced_arg = gr.Textbox(show_label=True, label="高级参数输入区", visible=False,
placeholder="这里是特殊函数插件的高级参数输入区").style(container=False)
self.switchy_bt = gr.Button(r"请先从插件列表中选择", variant="secondary")
def draw_setting_chat(self):
switch_model = get_conf('switch_model')[0]
with gr.TabItem('Settings', id='sett_tab'):
self.top_p = gr.Slider(minimum=-0, maximum=1.0, value=1.0, step=0.01, interactive=True,
label="Top-p (nucleus sampling)", ).style(container=False)
self.temperature = gr.Slider(minimum=-0, maximum=2.0, value=1.0, step=0.01, interactive=True,
label="Temperature", ).style(container=False)
self.max_length_sl = gr.Slider(minimum=256, maximum=4096, value=4096, step=1, interactive=True,
label="MaxLength", ).style(container=False)
self.pro_tf_slider = gr.Slider(minimum=0.01, maximum=1.0, value=0.70, step=0.01, interactive=True,
label="Term Frequency系数").style(container=False)
self.models_box = gr.CheckboxGroup(choices=switch_model['key'], value=switch_model['value'], label="对话模式")
self.system_prompt = gr.Textbox(show_label=True, lines=2, placeholder=f"System Prompt",
label="System prompt", value=self.initial_prompt)
# temp = gr.Markdown(self.description)
def draw_goals_auto(self):
with gr.Row():
self.ai_name = gr.Textbox(show_label=False, placeholder="给Ai一个名字").style(container=False)
with gr.Row():
self.ai_role = gr.Textbox(lines=5, show_label=False, placeholder="请输入你的需求").style(
container=False)
with gr.Row():
self.ai_goal_list = gr.Dataframe(headers=['Goals'], interactive=True, row_count=4,
col_count=(1, 'fixed'), type='array')
with gr.Row():
self.ai_budget = gr.Number(show_label=False, value=0.0,
info="关于本次项目的预算,超过预算自动停止,默认无限").style(container=False)
def draw_next_auto(self):
with gr.Row():
self.text_continue = gr.Textbox(visible=False, show_label=False,
placeholder="请根据提示输入执行命令").style(container=False)
with gr.Row():
self.submit_start = gr.Button("Start", variant='primary')
self.submit_next = gr.Button("Next", visible=False, variant='primary')
self.submit_stop = gr.Button("Stop", variant="stop")
self.agent_obj = gr.State({'obj': None, "start": self.submit_start,
"next": self.submit_next, "text": self.text_continue})
def signals_input_setting(self):
# 注册input
self.input_combo = [self.cookies, self.max_length_sl, self.md_dropdown,
self.input_copy, self.top_p, self.temperature, self.chatbot, self.history,
self.system_prompt, self.models_box, self.plugin_advanced_arg]
self.output_combo = [self.cookies, self.chatbot, self.history, self.status]
self.predict_args = dict(fn=ArgsGeneralWrapper(predict), inputs=self.input_combo, outputs=self.output_combo)
self.clear_agrs = dict(fn=self.__clear_input, inputs=[self.txt], outputs=[self.txt, self.input_copy,
self.examples_column])
# 提交按钮、重置按钮
self.cancel_handles.append(self.txt.submit(**self.clear_agrs).then(**self.predict_args))
self.cancel_handles.append(self.submitBtn.click(**self.clear_agrs).then(**self.predict_args))
# self.cpopyBtn.click(fn=func_box.copy_result, inputs=[self.history], outputs=[self.status])
self.resetBtn.click(lambda: ([], [], "已重置"), None, [self.chatbot, self.history, self.status])
def signals_function(self):
# 基础功能区的回调函数注册
for k in functional:
self.click_handle = functional[k]["Button"].click(**self.clear_agrs).then(fn=ArgsGeneralWrapper(predict),
inputs=[*self.input_combo, gr.State(True), gr.State(k)],
outputs=self.output_combo)
self.cancel_handles.append(self.click_handle)
def signals_public(self):
# 文件上传区接收文件后与chatbot的互动
self.file_upload.upload(on_file_uploaded, [self.file_upload, self.chatbot, self.txt], [self.chatbot, self.txt])
# 函数插件-固定按钮区
for k in crazy_fns:
if not crazy_fns[k].get("AsButton", True): continue
self.click_handle = crazy_fns[k]["Button"].click(**self.clear_agrs).then(
ArgsGeneralWrapper(crazy_fns[k]["Function"]),
[*self.input_combo, gr.State(PORT), gr.State(crazy_fns[k].get('Parameters', False))],
self.output_combo)
self.click_handle.then(on_report_generated, [self.cookies, self.file_upload, self.chatbot],
[self.cookies, self.file_upload, self.chatbot])
# self.click_handle.then(fn=lambda x: '', inputs=[], outputs=self.txt)
self.cancel_handles.append(self.click_handle)
# 函数插件-下拉菜单与随变按钮的互动
def on_dropdown_changed(k):
# 按钮颜色随变
variant = crazy_fns[k]["Color"] if "Color" in crazy_fns[k] else "secondary"
ret = {self.switchy_bt: self.switchy_bt.update(value=k, variant=variant)}
# 参数取随变
fns_value = func_box.txt_converter_json(str(crazy_fns[k].get('Parameters', '')))
fns_lable = f"插件[{k}]的高级参数说明:\n" + crazy_fns[k].get("ArgsReminder", f"没有提供高级参数功能说明")
temp_dict = dict(visible=True, interactive=True, value=str(fns_value), label=fns_lable)
# 是否唤起高级插件参数区
if crazy_fns[k].get("AdvancedArgs", False):
ret.update({self.plugin_advanced_arg: gr.update(**temp_dict)})
else:
ret.update({self.plugin_advanced_arg: gr.update(visible=False, label=f"插件[{k}]不需要高级参数。")})
return ret
self.dropdown.select(on_dropdown_changed, [self.dropdown], [self.switchy_bt, self.plugin_advanced_arg])
# 随变按钮的回调函数注册
def route(k, ipaddr: gr.Request, *args, **kwargs):
if k in [r"打开插件列表", r"请先从插件列表中选择"]: return
append = list(args)
append[-2] = func_box.txt_converter_json(append[-2])
append.insert(-1, ipaddr)
args = tuple(append)
yield from ArgsGeneralWrapper(crazy_fns[k]["Function"])(*args, **kwargs)
self.click_handle = self.switchy_bt.click(**self.clear_agrs).then(route, [self.switchy_bt, *self.input_combo, gr.State(PORT)], self.output_combo)
self.click_handle.then(on_report_generated, [self.cookies, self.file_upload, self.chatbot],
[self.cookies, self.file_upload, self.chatbot])
self.cancel_handles.append(self.click_handle)
# 终止按钮的回调函数注册
self.stopBtn.click(fn=None, inputs=None, outputs=None, cancels=self.cancel_handles)
def on_md_dropdown_changed(k):
return {self.chatbot: gr.update(label="当前模型:" + k)}
self.md_dropdown.select(on_md_dropdown_changed, [self.md_dropdown], [self.chatbot])
def signals_auto_input(self):
self.auto_input_combo = [self.ai_name, self.ai_role, self.ai_goal_list, self.ai_budget,
self.cookies, self.chatbot, self.history,
self.agent_obj]
self.auto_output_combo = [self.cookies, self.chatbot, self.history, self.status,
self.agent_obj, self.submit_start, self.submit_next, self.text_continue]
# gradio的inbrowser触发不太稳定回滚代码到原始的浏览器打开函数
def auto_opentab_delay(self, is_open=False):
import threading, webbrowser, time
print(f"如果浏览器没有自动打开请复制并转到以下URL")
print(f"\t(亮色主题): http://localhost:{PORT}")
print(f"\t(暗色主题): {self.__url}/?__theme=dark")
if is_open:
def open():
time.sleep(2) # 打开浏览器
webbrowser.open_new_tab(f"http://localhost:{PORT}/?__theme=dark")
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()
def main(self):
with gr.Blocks(title="Chatbot for KSO ", theme=set_theme, analytics_enabled=False, css=custom_css) as self.demo:
# 绘制页面title
self.draw_title()
# 绘制一个ROWrow会让底下的元素自动排成一行
with gr.Row().style(justify='between'):
# 绘制列1
with gr.Column(scale=44):
with gr.Tabs() as self.tabs_copilot:
# 绘制对话模组
with gr.TabItem('Chat-Copilot'):
with gr.Row():
# self.cpopyBtn = gr.Button("复制回答", variant="secondary").style(size="sm")
self.resetBtn = gr.Button("新建对话", variant="primary", elem_id='empty_btn').style(
size="sm")
self.stopBtn = gr.Button("中止对话", variant="stop").style(size="sm")
with gr.Tabs() as self.tabs_inputs:
self.draw_function_chat()
self.draw_public_chat()
self.draw_setting_chat()
# 绘制autogpt模组
with gr.TabItem('Auto-GPT'):
self.draw_next_auto()
self.draw_goals_auto()
# 绘制列2
with gr.Column(scale=100):
with gr.Tabs() as self.tabs_chatbot:
with gr.TabItem('Chatbot', id='chatbot') as self.chat_tab:
# self.draw_chatbot()
pass
with gr.TabItem('Prompt检索/编辑') as self.prompt_tab:
self.draw_prompt()
with self.chat_tab: # 使用 gr.State()对组件进行拷贝时如果之前绘制了Markdown格式会导致启动崩溃,所以将 markdown相关绘制放在最后
self.draw_chatbot()
self.draw_examples()
with self.prompt_tab:
self.draw_temp_edit()
# 函数注册需要在Blocks下进行
self.signals_sm_btn()
self.signals_input_setting()
self.signals_function()
self.signals_prompt_func()
self.signals_public()
self.signals_prompt_edit()
# self.signals_auto_input()
adv_plugins = gr.State([i for i in crazy_fns])
self.demo.load(fn=func_box.refresh_load_data, postprocess=False,
inputs=[self.chatbot, self.history, self.pro_fp_state, adv_plugins],
outputs=[self.pro_func_prompt, self.pro_fp_state, self.chatbot, self.history, self.guidance_plugins, self.guidance_plugins_state])
# Start
self.auto_opentab_delay()
self.demo.queue(concurrency_count=CONCURRENT_COUNT).launch(server_name="0.0.0.0", server_port=PORT, auth=AUTHENTICATION,
blocked_paths=["config.py", "config_private.py", "docker-compose.yml", "Dockerfile"])
def check_proxy_free():
proxy_state = func_box.Shell(f'lsof -i :{PORT}').read()[1].splitlines()
if proxy_state != ["", ""]:
print('Kill Old Server')
for i in proxy_state[1:]:
func_box.Shell(f'kill -9 {i.split()[1]}').read()
import time
time.sleep(5)
if __name__ == '__main__':
# PORT = find_free_port() if WEB_PORT <= 0 else WEB_PORT
PORT = LOCAL_PORT if WEB_PORT <= 0 else WEB_PORT
check_proxy_free()
ChatBot().main()
gr.close_all()
check_proxy_free()

View File

@ -11,7 +11,9 @@ def check_proxy(proxies):
country = data['country_name']
result = f"代理配置 {proxies_https}, 代理所在地:{country}"
elif 'error' in data:
result = f"代理配置 {proxies_https}, 代理所在地:未知"
result = f"代理配置 {proxies_https}, 代理所在地:未知IP查询频率受限"
else:
result = f"代理配置 {proxies_https}, 代理数据解析失败:{data}"
print(result)
return result
except:
@ -137,7 +139,7 @@ def auto_update(raise_error=False):
else:
return
except:
msg = '自动更新程序:已禁用'
msg = '自动更新程序:已禁用。建议排查:代理网络配置。'
if raise_error:
from toolbox import trimmed_format_exc
msg += trimmed_format_exc()

125
config.py
View File

@ -1,36 +1,27 @@
# [step 1]>> 例如: API_KEY = "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" 此key无效
API_KEY = "sk-此处填API密钥" # 可同时填写多个API-KEY用英文逗号分割例如API_KEY = "sk-openaikey1,sk-openaikey2,fkxxxx-api2dkey1,fkxxxx-api2dkey2"
"""
以下所有配置也都支持利用环境变量覆写环境变量配置格式见docker-compose.yml。
读取优先级:环境变量 > config_private.py > config.py
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
All the following configurations also support using environment variables to override,
and the environment variable configuration format can be seen in docker-compose.yml.
Configuration reading priority: environment variable > config_private.py > config.py
"""
# [step 1]>> API_KEY = "sk-123456789xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx123456789"。极少数情况下还需要填写组织格式如org-123456789abcdefghijklmno的请向下翻找 API_ORG 设置项
API_KEY = "此处填API密钥" # 可同时填写多个API-KEY用英文逗号分割例如API_KEY = "sk-openaikey1,sk-openaikey2,fkxxxx-api2dkey3,azure-apikey4"
prompt_list = {'key': ['所有人', '个人'], 'value': []}
switch_model = {'key': ['input加密', '隐私模式'], 'value': ['input加密']}
private_key = 'uhA51pHtjisfjij'
import func_box
import os
devs_document = "/file="+os.path.join(func_box.base_path, 'README.md')
#增加关于AZURE的配置信息 可以在AZURE网页中找到
AZURE_ENDPOINT = "https://你的api名称.openai.azure.com/"
AZURE_API_KEY = "填入azure openai api的密钥"
AZURE_API_VERSION = "填入api版本"
AZURE_ENGINE = "填入ENGINE"
# [step 2]>> 改为True应用代理如果直接在海外服务器部署此处不修改
USE_PROXY = False
LOCAL_PORT = 7891
if USE_PROXY:
# 填写格式是 [协议]:// [地址] :[端口]填写之前不要忘记把USE_PROXY改成True如果直接在海外服务器部署此处不修改
# 例如 "socks5h://localhost:11284"
# [协议] 常见协议无非socks5h/http; 例如 v2**y 和 ss* 的默认本地协议是socks5h; 而cl**h 的默认本地协议是http
# [地址] 懂的都懂不懂就填localhost或者127.0.0.1肯定错不了localhost意思是代理软件安装在本机上
# [端口] 在代理软件的设置里找。虽然不同的代理软件界面不一样,但端口号都应该在最显眼的位置上
# 代理网络的地址,打开你的*学*网软件查看代理的协议(socks5/http)、地址(localhost)和端口(11284)
"""
填写格式是 [协议]:// [地址] :[端口]填写之前不要忘记把USE_PROXY改成True如果直接在海外服务器部署此处不修改
<配置教程&视频教程> https://github.com/binary-husky/gpt_academic/issues/1>
[协议] 常见协议无非socks5h/http; 例如 v2**y 和 ss* 的默认本地协议是socks5h; 而cl**h 的默认本地协议是http
[地址] 懂的都懂不懂就填localhost或者127.0.0.1肯定错不了localhost意思是代理软件安装在本机上
[端口] 在代理软件的设置里找。虽然不同的代理软件界面不一样,但端口号都应该在最显眼的位置上
"""
# 代理网络的地址,打开你的*学*网软件查看代理的协议(socks5h / http)、地址(localhost)和端口(11284)
proxies = {
# [协议]:// [地址] :[端口]
"http": "socks5h://localhost:11284", # 再例如 "http": "http://127.0.0.1:7890",
@ -39,78 +30,84 @@ if USE_PROXY:
else:
proxies = None
# [step 3]>> 多线程函数插件中默认允许多少路线程同时访问OpenAI。Free trial users的限制是每分钟3次Pay-as-you-go users的限制是每分钟3500次
# 一言以蔽之免费用户填3OpenAI绑了信用卡的用户可以填 16 或者更高。提高限制请查询https://platform.openai.com/docs/guides/rate-limits/overview
# ------------------------------------ 以下配置可以优化体验, 但大部分场合下并不需要修改 ------------------------------------
# 重新URL重新定向实现更换API_URL的作用常规情况下不要修改!! 高危设置通过修改此设置您将把您的API-KEY和对话隐私完全暴露给您设定的中间人
# 格式 API_URL_REDIRECT = {"https://api.openai.com/v1/chat/completions": "在这里填写重定向的api.openai.com的URL"}
# 例如 API_URL_REDIRECT = {"https://api.openai.com/v1/chat/completions":"https://reverse-proxy-url/v1/chat/completions"}
API_URL_REDIRECT = {}
# 多线程函数插件中默认允许多少路线程同时访问OpenAI。Free trial users的限制是每分钟3次Pay-as-you-go users的限制是每分钟3500次
# 一言以蔽之免费5刀用户填3OpenAI绑了信用卡的用户可以填 16 或者更高。提高限制请查询https://platform.openai.com/docs/guides/rate-limits/overview
DEFAULT_WORKER_NUM = 3
# [step 3]>> 以下配置可以优化体验,但大部分场合下并不需要修改 # 废弃了移步到theme.py 的 #main_chatbot中修改
# 对话窗的高度
CHATBOT_HEIGHT = 1115
# 主题
THEME = "Default"
# 代码高亮
CODE_HIGHLIGHT = True
# 窗口布局
LAYOUT = "LEFT-RIGHT" # "LEFT-RIGHT"(左右布局) # "TOP-DOWN"(上下布局)
DARK_MODE = True # "LEFT-RIGHT"(左右布局) # "TOP-DOWN"(上下布局)
LAYOUT = "LEFT-RIGHT" # "LEFT-RIGHT"(左右布局) # "TOP-DOWN"(上下布局)
DARK_MODE = True # 暗色模式 / 亮色模式
# 发送请求到OpenAI后等待多久判定为超时
TIMEOUT_SECONDS = 30
# 网页的端口, -1代表随机端口
WEB_PORT = -1
# 如果OpenAI不响应网络卡顿、代理失败、KEY失效重试的次数限制
MAX_RETRY = 2
# 模型选择是 (注意: LLM_MODEL是默认选中的模型, 同时它必须被包含在AVAIL_LLM_MODELS切换列表中 )
LLM_MODEL = "gpt-3.5-turbo" # 可选 ↓↓↓
AVAIL_LLM_MODELS = ["gpt-3.5-turbo-16k", "gpt-3.5-turbo", "azure-gpt35", "api2d-gpt-3.5-turbo", "gpt-4", "api2d-gpt-4", "chatglm", "moss", "newbing", "newbing-free", "stack-claude"]
# P.S. 其他可用的模型还包括 ["newbing-free", "jittorllms_rwkv", "jittorllms_pangualpha", "jittorllms_llama"]
# 模型选择是 (注意: LLM_MODEL是默认选中的模型, 它*必须*被包含在AVAIL_LLM_MODELS列表中 )
LLM_MODEL = "gpt-3.5-turbo" # 可选 ↓↓↓
AVAIL_LLM_MODELS = ["gpt-3.5-turbo-16k", "gpt-3.5-turbo", "azure-gpt-3.5", "api2d-gpt-3.5-turbo", "gpt-4", "api2d-gpt-4", "chatglm", "moss", "newbing", "stack-claude"]
# P.S. 其他可用的模型还包括 ["gpt-3.5-turbo-0613", "gpt-3.5-turbo-16k-0613", "newbing-free", "jittorllms_rwkv", "jittorllms_pangualpha", "jittorllms_llama"]
# ChatGLM(2) Finetune Model Path 如果使用ChatGLM2微调模型需要把"chatglmft"加入AVAIL_LLM_MODELS中
ChatGLM_PTUNING_CHECKPOINT = "" # 例如"/home/hmp/ChatGLM2-6B/ptuning/output/6b-pt-128-1e-2/checkpoint-100"
# 本地LLM模型如ChatGLM的执行方式 CPU/GPU
LOCAL_MODEL_DEVICE = "cpu" # 可选 "cuda"
# OpenAI的API_URL
API_URL = "https://api.openai.com/v1/chat/completions"
PROXY_API_URL = '' # 你的网关应用
# 设置gradio的并行线程数不需要修改
CONCURRENT_COUNT = 100
# 是否在提交时自动清空输入框
AUTO_CLEAR_TXT = False
# 色彩主体,可选 ["Default", "Chuanhu-Small-and-Beautiful"]
THEME = "Default"
# 加一个live2d装饰
ADD_WAIFU = False
# 川虎JS
ADD_CHUANHU = True
# 设置用户名和密码不需要修改相关功能不稳定与gradio版本和网络都相关如果本地使用不建议加这个
# [("username", "password"), ("username2", "password2"), ...]
AUTHENTICATION = []
# 重新URL重新定向实现更换API_URL的作用常规情况下不要修改!!
# 高危设置通过修改此设置您将把您的API-KEY和对话隐私完全暴露给您设定的中间人
# 格式 {"https://api.openai.com/v1/chat/completions": "在这里填写重定向的api.openai.com的URL"}
# 例如 API_URL_REDIRECT = {"https://api.openai.com/v1/chat/completions": "https://ai.open.com/api/conversation"}
API_URL_REDIRECT = {}
# 如果需要在二级路径下运行(常规情况下,不要修改!!需要配合修改main.py才能生效!
CUSTOM_PATH = "/"
# 如果需要使用newbing把newbing的长长的cookie放到这里
NEWBING_STYLE = "creative" # ["creative", "balanced", "precise"]
# 从现在起,如果您调用"newbing-free"模型则无需填写NEWBING_COOKIES
NEWBING_COOKIES = """
your bing cookies here
"""
# 极少数情况下openai的官方KEY需要伴随组织编码格式如org-xxxxxxxxxxxxxxxxxxxxxxxx使用
API_ORG = ""
# 如果需要使用Slack Claude使用教程详情见 request_llm/README.md
SLACK_CLAUDE_BOT_ID = ''
@ -118,7 +115,19 @@ SLACK_CLAUDE_USER_TOKEN = ''
# 如果需要使用AZURE 详情请见额外文档 docs\use_azure.md
AZURE_ENDPOINT = "https://你的api名称.openai.azure.com/"
AZURE_API_KEY = "填入azure openai api的密钥"
AZURE_API_VERSION = "填入api版本"
AZURE_ENGINE = "填入ENGINE"
AZURE_ENDPOINT = "https://你亲手写的api名称.openai.azure.com/"
AZURE_API_KEY = "填入azure openai api的密钥" # 建议直接在API_KEY处填写该选项即将被弃用
AZURE_ENGINE = "填入你亲手写的部署名" # 读 docs\use_azure.md
# 使用Newbing
NEWBING_STYLE = "creative" # ["creative", "balanced", "precise"]
NEWBING_COOKIES = """
put your new bing cookies here
"""
# 阿里云实时语音识别 配置难度较高 仅建议高手用户使用 参考 https://help.aliyun.com/document_detail/450255.html
ENABLE_AUDIO = False
ALIYUN_TOKEN="" # 例如 f37f30e0f9934c34a992f6f64f7eba4f
ALIYUN_APPKEY="" # 例如 RoPlZrM88DnAFkZK

View File

@ -61,7 +61,7 @@ def get_core_functions():
},
"找图片": {
"Prefix": r"我需要你找一张网络图片。使用Unsplash API(https://source.unsplash.com/960x640/?<英语关键词>)获取图片URL" +
r"然后请使用Markdown格式封装并且不要有反斜线不要用代码块。现在请按以下描述给我发送图片" + "\n",
r"然后请使用Markdown格式封装并且不要有反斜线不要用代码块。现在请按以下描述给我发送图片" + "\n\n",
"Suffix": r"",
"Visible": False,
},
@ -76,11 +76,3 @@ def get_core_functions():
"Suffix": r"",
}
}
def get_guidance():
pass
def get_guidance():
pass

View File

@ -20,28 +20,19 @@ def get_crazy_functions():
from crazy_functions.解析项目源代码 import 解析一个Lua项目
from crazy_functions.解析项目源代码 import 解析一个CSharp项目
from crazy_functions.总结word文档 import 总结word文档
from crazy_functions.辅助回答 import 猜你想问
from crazy_functions.解析JupyterNotebook import 解析ipynb文件
from crazy_functions.对话历史存档 import 对话历史存档
from crazy_functions.对话历史存档 import 载入对话历史存档
from crazy_functions.对话历史存档 import 删除所有本地对话历史记录
from crazy_functions.批量Markdown翻译 import Markdown英译中
function_plugins = {
"猜你想问": {
"Function": HotReload(猜你想问)
},
"解析整个Python项目": {
"Color": "primary", # 按钮颜色
"AsButton": False,
"Color": "stop", # 按钮颜色
"Function": HotReload(解析一个Python项目)
},
"保存当前的对话": {
"AsButton": True,
"Function": HotReload(对话历史存档)
},
"载入对话历史存档(先上传存档或输入路径)": {
"Color": "primary",
"Color": "stop",
"AsButton":False,
"Function": HotReload(载入对话历史存档)
},
@ -49,78 +40,77 @@ def get_crazy_functions():
"AsButton":False,
"Function": HotReload(删除所有本地对话历史记录)
},
"[测试功能] 解析Jupyter Notebook文件": {
"Color": "primary",
"AsButton": False,
"Color": "stop",
"AsButton":False,
"Function": HotReload(解析ipynb文件),
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "若输入0则不解析notebook中的Markdown块", # 高级参数输入区的显示提示
},
"批量总结Word文档": {
"AsButton": False,
"Color": "primary",
"Color": "stop",
"Function": HotReload(总结word文档)
},
"解析整个C++项目头文件": {
"Color": "primary", # 按钮颜色
"Color": "stop", # 按钮颜色
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(解析一个C项目的头文件)
},
"解析整个C++项目(.cpp/.hpp/.c/.h": {
"Color": "primary", # 按钮颜色
"Color": "stop", # 按钮颜色
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(解析一个C项目)
},
"解析整个Go项目": {
"Color": "primary", # 按钮颜色
"Color": "stop", # 按钮颜色
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(解析一个Golang项目)
},
"解析整个Rust项目": {
"Color": "primary", # 按钮颜色
"Color": "stop", # 按钮颜色
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(解析一个Rust项目)
},
"解析整个Java项目": {
"Color": "primary", # 按钮颜色
"Color": "stop", # 按钮颜色
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(解析一个Java项目)
},
"解析整个前端项目js,ts,css等": {
"Color": "primary", # 按钮颜色
"Color": "stop", # 按钮颜色
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(解析一个前端项目)
},
"解析整个Lua项目": {
"Color": "primary", # 按钮颜色
"Color": "stop", # 按钮颜色
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(解析一个Lua项目)
},
"解析整个CSharp项目": {
"Color": "primary", # 按钮颜色
"Color": "stop", # 按钮颜色
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(解析一个CSharp项目)
},
"读Tex论文写摘要": {
"Color": "primary", # 按钮颜色
"AsButton": False, # 加入下拉菜单中
"Color": "stop", # 按钮颜色
"Function": HotReload(读文章写摘要)
},
"Markdown/Readme英译中": {
# HotReload 的意思是热更新,修改函数插件代码后,不需要重启程序,代码直接生效
"Color": "primary",
"AsButton": False,
"Color": "stop",
"Function": HotReload(Markdown英译中)
},
"批量生成函数注释": {
"Color": "primary", # 按钮颜色
"Color": "stop", # 按钮颜色
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(批量生成函数注释)
},
"保存当前的对话": {
"Function": HotReload(对话历史存档)
},
"[多线程Demo] 解析此项目本身(源码自译解)": {
"Function": HotReload(解析项目本身),
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(解析项目本身)
},
# "[老旧的Demo] 把本项目源代码切换成全英文": {
# # HotReload 的意思是热更新,修改函数插件代码后,不需要重启程序,代码直接生效
@ -129,8 +119,7 @@ def get_crazy_functions():
# },
"[插件demo] 历史上的今天": {
# HotReload 的意思是热更新,修改函数插件代码后,不需要重启程序,代码直接生效
"Function": HotReload(高阶功能模板函数),
"AsButton": False,
"Function": HotReload(高阶功能模板函数)
},
}
@ -149,69 +138,69 @@ def get_crazy_functions():
function_plugins.update({
"批量翻译PDF文档多线程": {
"Color": "primary",
"AsButton": False, # 加入下拉菜单中
"Color": "stop",
"AsButton": True, # 加入下拉菜单中
"Function": HotReload(批量翻译PDF文档)
},
"询问多个GPT模型": {
"Color": "primary", # 按钮颜色
"Color": "stop", # 按钮颜色
"Function": HotReload(同时问询)
},
"[测试功能] 批量总结PDF文档": {
"Color": "primary",
"Color": "stop",
"AsButton": False, # 加入下拉菜单中
# HotReload 的意思是热更新,修改函数插件代码后,不需要重启程序,代码直接生效
"Function": HotReload(批量总结PDF文档)
},
# "[测试功能] 批量总结PDF文档pdfminer": {
# "Color": "primary",
# "Color": "stop",
# "AsButton": False, # 加入下拉菜单中
# "Function": HotReload(批量总结PDF文档pdfminer)
# },
"谷歌学术检索助手输入谷歌学术搜索页url": {
"Color": "primary",
"Color": "stop",
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(谷歌检索小助手)
},
"理解PDF文档内容 模仿ChatPDF": {
# HotReload 的意思是热更新,修改函数插件代码后,不需要重启程序,代码直接生效
"Color": "primary",
"AsButton": True, # 加入下拉菜单中
"Color": "stop",
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(理解PDF文档内容标准文件输入)
},
"英文Latex项目全文润色输入路径或上传压缩包": {
# HotReload 的意思是热更新,修改函数插件代码后,不需要重启程序,代码直接生效
"Color": "primary",
"Color": "stop",
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(Latex英文润色)
},
"英文Latex项目全文纠错输入路径或上传压缩包": {
# HotReload 的意思是热更新,修改函数插件代码后,不需要重启程序,代码直接生效
"Color": "primary",
"Color": "stop",
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(Latex英文纠错)
},
"中文Latex项目全文润色输入路径或上传压缩包": {
# HotReload 的意思是热更新,修改函数插件代码后,不需要重启程序,代码直接生效
"Color": "primary",
"Color": "stop",
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(Latex中文润色)
},
"Latex项目全文中译英输入路径或上传压缩包": {
# HotReload 的意思是热更新,修改函数插件代码后,不需要重启程序,代码直接生效
"Color": "primary",
"Color": "stop",
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(Latex中译英)
},
"Latex项目全文英译中输入路径或上传压缩包": {
# HotReload 的意思是热更新,修改函数插件代码后,不需要重启程序,代码直接生效
"Color": "primary",
"Color": "stop",
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(Latex英译中)
},
"批量Markdown中译英输入路径或上传压缩包": {
# HotReload 的意思是热更新,修改函数插件代码后,不需要重启程序,代码直接生效
"Color": "primary",
"Color": "stop",
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(Markdown中译英)
},
@ -221,11 +210,12 @@ def get_crazy_functions():
###################### 第三组插件 ###########################
# [第三组插件]: 尚未充分测试的函数插件
try:
from crazy_functions.下载arxiv论文翻译摘要 import 下载arxiv论文并翻译摘要
function_plugins.update({
"一键下载arxiv论文并翻译摘要先在input输入编号如1812.10695": {
"Color": "primary",
"Color": "stop",
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(下载arxiv论文并翻译摘要)
}
@ -237,7 +227,7 @@ def get_crazy_functions():
from crazy_functions.联网的ChatGPT import 连接网络回答问题
function_plugins.update({
"连接网络回答问题(输入问题后点击该插件,需要访问谷歌)": {
"Color": "primary",
"Color": "stop",
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(连接网络回答问题)
}
@ -245,7 +235,7 @@ def get_crazy_functions():
from crazy_functions.联网的ChatGPT_bing版 import 连接bing搜索回答问题
function_plugins.update({
"连接网络回答问题中文Bing版输入问题后点击该插件": {
"Color": "primary",
"Color": "stop",
"AsButton": False, # 加入下拉菜单中
"Function": HotReload(连接bing搜索回答问题)
}
@ -257,7 +247,7 @@ def get_crazy_functions():
from crazy_functions.解析项目源代码 import 解析任意code项目
function_plugins.update({
"解析项目源代码(手动指定和筛选源代码文件类型)": {
"Color": "primary",
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "输入时用逗号隔开, *代表通配符, 加了^代表不匹配; 不输入代表全部匹配。例如: \"*.c, ^*.cpp, config.toml, ^*.toml\"", # 高级参数输入区的显示提示
@ -271,7 +261,7 @@ def get_crazy_functions():
from crazy_functions.询问多个大语言模型 import 同时问询_指定模型
function_plugins.update({
"询问多个GPT模型手动指定询问哪些模型": {
"Color": "primary",
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "支持任意数量的llm接口用&符号分隔。例如chatglm&gpt-3.5-turbo&api2d-gpt-4", # 高级参数输入区的显示提示
@ -285,7 +275,7 @@ def get_crazy_functions():
from crazy_functions.图片生成 import 图片生成
function_plugins.update({
"图片生成先切换模型到openai或api2d": {
"Color": "primary",
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "在这里输入分辨率, 如256x256默认", # 高级参数输入区的显示提示
@ -299,7 +289,7 @@ def get_crazy_functions():
from crazy_functions.总结音视频 import 总结音视频
function_plugins.update({
"批量总结音视频(输入路径或上传压缩包)": {
"Color": "primary",
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True,
"ArgsReminder": "调用openai api 使用whisper-1模型, 目前支持的格式:mp4, m4a, wav, mpga, mpeg, mp3。此处可以输入解析提示例如解析为简体中文默认",
@ -309,51 +299,11 @@ def get_crazy_functions():
except:
print('Load function plugin failed')
from crazy_functions.解析项目源代码 import 解析任意code项目
function_plugins.update({
"解析项目源代码(手动指定和筛选源代码文件类型)": {
"Color": "primary",
"AsButton": False,
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "输入时用逗号隔开, *代表通配符, 加了^代表不匹配; 不输入代表全部匹配。例如: \"*.c, ^*.cpp, config.toml, ^*.toml\"", # 高级参数输入区的显示提示
"Function": HotReload(解析任意code项目)
},
})
from crazy_functions.询问多个大语言模型 import 同时问询_指定模型
function_plugins.update({
"询问多个GPT模型手动指定询问哪些模型": {
"Color": "primary",
"AsButton": False,
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "支持任意数量的llm接口用&符号分隔。例如chatglm&gpt-3.5-turbo&api2d-gpt-4", # 高级参数输入区的显示提示
"Function": HotReload(同时问询_指定模型)
},
})
from crazy_functions.图片生成 import 图片生成
function_plugins.update({
"图片生成先切换模型到openai或api2d": {
"Color": "primary",
"AsButton": True,
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "在这里输入分辨率, 如'256x256'(默认), '512x512', '1024x1024'", # 高级参数输入区的显示提示
"Function": HotReload(图片生成)
},
})
from crazy_functions.总结音视频 import 总结音视频
function_plugins.update({
"批量总结音视频(输入路径或上传压缩包)": {
"Color": "primary",
"AsButton": False,
"AdvancedArgs": True,
"ArgsReminder": "调用openai api 使用whisper-1模型, 目前支持的格式:mp4, m4a, wav, mpga, mpeg, mp3。此处可以输入解析提示例如解析为简体中文默认",
"Function": HotReload(总结音视频)
}
})
try:
from crazy_functions.数学动画生成manim import 动画生成
function_plugins.update({
"数学动画生成Manim": {
"Color": "primary",
"Color": "stop",
"AsButton": False,
"Function": HotReload(动画生成)
}
@ -365,7 +315,7 @@ def get_crazy_functions():
from crazy_functions.批量Markdown翻译 import Markdown翻译指定语言
function_plugins.update({
"Markdown翻译手动指定语言": {
"Color": "primary",
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True,
"ArgsReminder": "请输入要翻译成哪种语言默认为Chinese。",
@ -379,7 +329,7 @@ def get_crazy_functions():
from crazy_functions.Langchain知识库 import 知识库问答
function_plugins.update({
"[功能尚不稳定] 构建知识库(请先上传文件素材)": {
"Color": "primary",
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True,
"ArgsReminder": "待注入的知识库名称id, 默认为default",
@ -393,7 +343,7 @@ def get_crazy_functions():
from crazy_functions.Langchain知识库 import 读取知识库作答
function_plugins.update({
"[功能尚不稳定] 知识库问答": {
"Color": "primary",
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True,
"ArgsReminder": "待提取的知识库名称id, 默认为default, 您需要首先调用构建知识库",
@ -402,12 +352,38 @@ def get_crazy_functions():
})
except:
print('Load function plugin failed')
try:
from crazy_functions.交互功能函数模板 import 交互功能模板函数
function_plugins.update({
"交互功能模板函数": {
"Color": "stop",
"AsButton": False,
"Function": HotReload(交互功能模板函数)
}
})
except:
print('Load function plugin failed')
# try:
# from crazy_functions.chatglm微调工具 import 微调数据集生成
# function_plugins.update({
# "黑盒模型学习: 微调数据集生成 (先上传数据集)": {
# "Color": "stop",
# "AsButton": False,
# "AdvancedArgs": True,
# "ArgsReminder": "针对数据集输入(如 绿帽子*深蓝色衬衫*黑色运动裤)给出指令,例如您可以将以下命令复制到下方: --llm_to_learn=azure-gpt-3.5 --prompt_prefix='根据下面的服装类型提示想象一个穿着者对这个人外貌、身处的环境、内心世界、过去经历进行描写。要求100字以内用第二人称。' --system_prompt=''",
# "Function": HotReload(微调数据集生成)
# }
# })
# except:
# print('Load function plugin failed')
try:
from crazy_functions.Latex输出PDF结果 import Latex英文纠错加PDF对比
function_plugins.update({
"Latex英文纠错+高亮修正位置 [需Latex]": {
"Color": "primary",
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True,
"ArgsReminder": "如果有必要, 请在此处追加更细致的矫错指令(使用英文)。",
@ -416,23 +392,23 @@ def get_crazy_functions():
})
from crazy_functions.Latex输出PDF结果 import Latex翻译中文并重新编译PDF
function_plugins.update({
"Arixv翻译输入arxivID[需Latex]": {
"Color": "primary",
"Arixv论文精细翻译输入arxivID[需Latex]": {
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True,
"ArgsReminder":
"如果有必要, 请在此处给出自定义翻译命令, 解决部分词汇翻译不准确的问题。 "+
"ArgsReminder":
"如果有必要, 请在此处给出自定义翻译命令, 解决部分词汇翻译不准确的问题。 "+
"例如当单词'agent'翻译不准确时, 请尝试把以下指令复制到高级参数区: " + 'If the term "agent" is used in this section, it should be translated to "智能体". ',
"Function": HotReload(Latex翻译中文并重新编译PDF)
}
})
function_plugins.update({
"本地论文翻译上传Latex压缩包[需Latex]": {
"Color": "primary",
"本地Latex论文精细翻译上传Latex项目[需Latex]": {
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True,
"ArgsReminder":
"如果有必要, 请在此处给出自定义翻译命令, 解决部分词汇翻译不准确的问题。 "+
"ArgsReminder":
"如果有必要, 请在此处给出自定义翻译命令, 解决部分词汇翻译不准确的问题。 "+
"例如当单词'agent'翻译不准确时, 请尝试把以下指令复制到高级参数区: " + 'If the term "agent" is used in this section, it should be translated to "智能体". ',
"Function": HotReload(Latex翻译中文并重新编译PDF)
}
@ -440,11 +416,27 @@ def get_crazy_functions():
except:
print('Load function plugin failed')
try:
from toolbox import get_conf
ENABLE_AUDIO, = get_conf('ENABLE_AUDIO')
if ENABLE_AUDIO:
from crazy_functions.语音助手 import 语音助手
function_plugins.update({
"实时音频采集": {
"Color": "stop",
"AsButton": True,
"Function": HotReload(语音助手)
}
})
except:
print('Load function plugin failed')
# try:
# from crazy_functions.虚空终端 import 终端
# function_plugins.update({
# "超级终端": {
# "Color": "primary",
# "Color": "stop",
# "AsButton": False,
# # "AdvancedArgs": True,
# # "ArgsReminder": "",
@ -454,5 +446,4 @@ def get_crazy_functions():
# except:
# print('Load function plugin failed')
###################### 第n组插件 ###########################
return function_plugins

View File

@ -30,7 +30,7 @@ def 知识库问答(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_pro
)
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
from .crazy_utils import try_install_deps
try_install_deps(['zh_langchain==0.2.1'])
try_install_deps(['zh_langchain==0.2.1', 'pypinyin'])
# < --------------------读取参数--------------- >
if ("advanced_arg" in plugin_kwargs) and (plugin_kwargs["advanced_arg"] == ""): plugin_kwargs.pop("advanced_arg")

View File

@ -0,0 +1,141 @@
from toolbox import CatchException, update_ui, promote_file_to_downloadzone
from .crazy_utils import request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency
import datetime, json
def fetch_items(list_of_items, batch_size):
for i in range(0, len(list_of_items), batch_size):
yield list_of_items[i:i + batch_size]
def string_to_options(arguments):
import argparse
import shlex
# Create an argparse.ArgumentParser instance
parser = argparse.ArgumentParser()
# Add command-line arguments
parser.add_argument("--llm_to_learn", type=str, help="LLM model to learn", default="gpt-3.5-turbo")
parser.add_argument("--prompt_prefix", type=str, help="Prompt prefix", default='')
parser.add_argument("--system_prompt", type=str, help="System prompt", default='')
parser.add_argument("--batch", type=int, help="System prompt", default=50)
parser.add_argument("--pre_seq_len", type=int, help="pre_seq_len", default=50)
parser.add_argument("--learning_rate", type=float, help="learning_rate", default=2e-2)
parser.add_argument("--num_gpus", type=int, help="num_gpus", default=1)
parser.add_argument("--json_dataset", type=str, help="json_dataset", default="")
parser.add_argument("--ptuning_directory", type=str, help="ptuning_directory", default="")
# Parse the arguments
args = parser.parse_args(shlex.split(arguments))
return args
@CatchException
def 微调数据集生成(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
"""
txt 输入栏用户输入的文本,例如需要翻译的一段话,再例如一个包含了待处理文件的路径
llm_kwargs gpt模型参数如温度和top_p等一般原样传递下去就行
plugin_kwargs 插件模型的参数
chatbot 聊天显示框的句柄,用于显示给用户
history 聊天历史,前情提要
system_prompt 给gpt的静默提醒
web_port 当前软件运行的端口号
"""
history = [] # 清空历史,以免输入溢出
chatbot.append(("这是什么功能?", "[Local Message] 微调数据集生成"))
if ("advanced_arg" in plugin_kwargs) and (plugin_kwargs["advanced_arg"] == ""): plugin_kwargs.pop("advanced_arg")
args = plugin_kwargs.get("advanced_arg", None)
if args is None:
chatbot.append(("没给定指令", "退出"))
yield from update_ui(chatbot=chatbot, history=history); return
else:
arguments = string_to_options(arguments=args)
dat = []
with open(txt, 'r', encoding='utf8') as f:
for line in f.readlines():
json_dat = json.loads(line)
dat.append(json_dat["content"])
llm_kwargs['llm_model'] = arguments.llm_to_learn
for batch in fetch_items(dat, arguments.batch):
res = yield from request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency(
inputs_array=[f"{arguments.prompt_prefix}\n\n{b}" for b in (batch)],
inputs_show_user_array=[f"Show Nothing" for _ in (batch)],
llm_kwargs=llm_kwargs,
chatbot=chatbot,
history_array=[[] for _ in (batch)],
sys_prompt_array=[arguments.system_prompt for _ in (batch)],
max_workers=10 # OpenAI所允许的最大并行过载
)
with open(txt+'.generated.json', 'a+', encoding='utf8') as f:
for b, r in zip(batch, res[1::2]):
f.write(json.dumps({"content":b, "summary":r}, ensure_ascii=False)+'\n')
promote_file_to_downloadzone(txt+'.generated.json', rename_file='generated.json', chatbot=chatbot)
return
@CatchException
def 启动微调(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
"""
txt 输入栏用户输入的文本,例如需要翻译的一段话,再例如一个包含了待处理文件的路径
llm_kwargs gpt模型参数如温度和top_p等一般原样传递下去就行
plugin_kwargs 插件模型的参数
chatbot 聊天显示框的句柄,用于显示给用户
history 聊天历史,前情提要
system_prompt 给gpt的静默提醒
web_port 当前软件运行的端口号
"""
import subprocess
history = [] # 清空历史,以免输入溢出
chatbot.append(("这是什么功能?", "[Local Message] 微调数据集生成"))
if ("advanced_arg" in plugin_kwargs) and (plugin_kwargs["advanced_arg"] == ""): plugin_kwargs.pop("advanced_arg")
args = plugin_kwargs.get("advanced_arg", None)
if args is None:
chatbot.append(("没给定指令", "退出"))
yield from update_ui(chatbot=chatbot, history=history); return
else:
arguments = string_to_options(arguments=args)
pre_seq_len = arguments.pre_seq_len # 128
learning_rate = arguments.learning_rate # 2e-2
num_gpus = arguments.num_gpus # 1
json_dataset = arguments.json_dataset # 't_code.json'
ptuning_directory = arguments.ptuning_directory # '/home/hmp/ChatGLM2-6B/ptuning'
command = f"torchrun --standalone --nnodes=1 --nproc-per-node={num_gpus} main.py \
--do_train \
--train_file AdvertiseGen/{json_dataset} \
--validation_file AdvertiseGen/{json_dataset} \
--preprocessing_num_workers 20 \
--prompt_column content \
--response_column summary \
--overwrite_cache \
--model_name_or_path THUDM/chatglm2-6b \
--output_dir output/clothgen-chatglm2-6b-pt-{pre_seq_len}-{learning_rate} \
--overwrite_output_dir \
--max_source_length 256 \
--max_target_length 256 \
--per_device_train_batch_size 1 \
--per_device_eval_batch_size 1 \
--gradient_accumulation_steps 16 \
--predict_with_generate \
--max_steps 100 \
--logging_steps 10 \
--save_steps 20 \
--learning_rate {learning_rate} \
--pre_seq_len {pre_seq_len} \
--quantization_bit 4"
process = subprocess.Popen(command, shell=True, cwd=ptuning_directory)
try:
process.communicate(timeout=3600*24)
except subprocess.TimeoutExpired:
process.kill()
return

View File

@ -211,22 +211,36 @@ def test_Latex():
# # for cookies, cb, hist, msg in silence_stdout(编译Latex)(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
# cli_printer.print(cb) # print(cb)
def test_chatglm_finetune():
from crazy_functions.chatglm微调工具 import 微调数据集生成, 启动微调
txt = 'build/dev.json'
plugin_kwargs = {"advanced_arg":"--llm_to_learn=gpt-3.5-turbo --prompt_prefix='根据下面的服装类型提示想象一个穿着者对这个人外貌、身处的环境、内心世界、人设进行描写。要求100字以内用第二人称。' --system_prompt=''" }
# for cookies, cb, hist, msg in (微调数据集生成)(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
# cli_printer.print(cb)
plugin_kwargs = {"advanced_arg":
" --pre_seq_len=128 --learning_rate=2e-2 --num_gpus=1 --json_dataset='t_code.json' --ptuning_directory='/home/hmp/ChatGLM2-6B/ptuning' " }
for cookies, cb, hist, msg in (启动微调)(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
cli_printer.print(cb)
# test_解析一个Python项目()
# test_Latex英文润色()
# test_Markdown中译英()
# test_批量翻译PDF文档()
# test_谷歌检索小助手()
# test_总结word文档()
# test_下载arxiv论文并翻译摘要()
# test_解析一个Cpp项目()
# test_联网回答问题()
# test_解析ipynb文件()
# test_数学动画生成manim()
# test_Langchain知识库()
# test_Langchain知识库读取()
if __name__ == "__main__":
test_Latex()
# test_解析一个Python项目()
# test_Latex英文润色()
# test_Markdown中译英()
# test_批量翻译PDF文档()
# test_谷歌检索小助手()
# test_总结word文档()
# test_下载arxiv论文并翻译摘要()
# test_解析一个Cpp项目()
# test_联网回答问题()
# test_解析ipynb文件()
# test_数学动画生成manim()
# test_Langchain知识库()
# test_Langchain知识库读取()
# test_Latex()
test_chatglm_finetune()
input("程序完成,回车退出。")
print("退出。")

View File

@ -130,6 +130,11 @@ def request_gpt_model_in_new_thread_with_ui_alive(
yield from update_ui(chatbot=chatbot, history=[]) # 如果最后成功了,则删除报错信息
return final_result
def can_multi_process(llm):
if llm.startswith('gpt-'): return True
if llm.startswith('api2d-'): return True
if llm.startswith('azure-'): return True
return False
def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency(
inputs_array, inputs_show_user_array, llm_kwargs,
@ -175,16 +180,16 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency(
except: max_workers = 8
if max_workers <= 0: max_workers = 3
# 屏蔽掉 chatglm的多线程可能会导致严重卡顿
if not (llm_kwargs['llm_model'].startswith('gpt-') or llm_kwargs['llm_model'].startswith('api2d-') or llm_kwargs['llm_model'].startswith('proxy-gpt')):
if not can_multi_process(llm_kwargs['llm_model']):
max_workers = 1
executor = ThreadPoolExecutor(max_workers=max_workers)
n_frag = len(inputs_array)
# 用户反馈
chatbot.append([None, ""])
chatbot.append(["请开始多线程操作。", ""])
yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面
# 跨线程传递
mutable = [[f"", time.time(), "等待中"] for _ in range(n_frag)]
mutable = [["", time.time(), "等待中"] for _ in range(n_frag)]
# 子线程任务
def _req_gpt(index, inputs, history, sys_prompt):
@ -272,8 +277,7 @@ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency(
' ', '.').replace('<br/>', '.....').replace('$', '.')+"`... ]"
observe_win.append(print_something_really_funny)
# 在前端打印些好玩的东西
stat_str = ''.join([f'`{inputs_show_user_array[thread_index][0:5]}...{inputs_show_user_array[thread_index][-5:]}`\t'
f'`{mutable[thread_index][2]}`: {obs}\n\n'
stat_str = ''.join([f'`{mutable[thread_index][2]}`: {obs}\n\n'
if not done else f'`{mutable[thread_index][2]}`\n\n'
for thread_index, done, obs in zip(range(len(worker_done)), worker_done, observe_win)])
# 在前端打印些好玩的东西

View File

@ -189,6 +189,18 @@ def rm_comments(main_file):
main_file = re.sub(r'(?<!\\)%.*', '', main_file) # 使用正则表达式查找半行注释, 并替换为空字符串
return main_file
def find_tex_file_ignore_case(fp):
dir_name = os.path.dirname(fp)
base_name = os.path.basename(fp)
if not base_name.endswith('.tex'): base_name+='.tex'
if os.path.exists(pj(dir_name, base_name)): return pj(dir_name, base_name)
# go case in-sensitive
import glob
for f in glob.glob(dir_name+'/*.tex'):
base_name_s = os.path.basename(fp)
if base_name_s.lower() == base_name.lower(): return f
return None
def merge_tex_files_(project_foler, main_file, mode):
"""
Merge Tex project recrusively
@ -197,15 +209,11 @@ def merge_tex_files_(project_foler, main_file, mode):
for s in reversed([q for q in re.finditer(r"\\input\{(.*?)\}", main_file, re.M)]):
f = s.group(1)
fp = os.path.join(project_foler, f)
if os.path.exists(fp):
# e.g., \input{srcs/07_appendix.tex}
with open(fp, 'r', encoding='utf-8', errors='replace') as fx:
c = fx.read()
else:
# e.g., \input{srcs/07_appendix}
assert os.path.exists(fp+'.tex'), f'即找不到{fp},也找不到{fp}.texTex源文件缺失'
with open(fp+'.tex', 'r', encoding='utf-8', errors='replace') as fx:
c = fx.read()
fp = find_tex_file_ignore_case(fp)
if fp:
with open(fp, 'r', encoding='utf-8', errors='replace') as fx: c = fx.read()
else:
raise RuntimeError(f'找不到{fp}Tex源文件缺失')
c = merge_tex_files_(project_foler, c, mode)
main_file = main_file[:s.span()[0]] + c + main_file[s.span()[1]:]
return main_file
@ -324,7 +332,7 @@ def split_subprocess(txt, project_folder, return_dict, opts):
# 吸收在42行以内的begin-end组合
text, mask = set_forbidden_text_begin_end(text, mask, r"\\begin\{([a-z\*]*)\}(.*?)\\end\{\1\}", re.DOTALL, limit_n_lines=42)
# 吸收匿名公式
text, mask = set_forbidden_text(text, mask, [ r"\$\$(.*?)\$\$", r"\\\[.*?\\\]" ], re.DOTALL)
text, mask = set_forbidden_text(text, mask, [ r"\$\$([^$]+)\$\$", r"\\\[.*?\\\]" ], re.DOTALL)
# 吸收其他杂项
text, mask = set_forbidden_text(text, mask, [ r"\\section\{(.*?)\}", r"\\section\*\{(.*?)\}", r"\\subsection\{(.*?)\}", r"\\subsubsection\{(.*?)\}" ])
text, mask = set_forbidden_text(text, mask, [ r"\\bibliography\{(.*?)\}", r"\\bibliographystyle\{(.*?)\}" ])
@ -657,7 +665,6 @@ def Latex精细分解与转化(file_manifest, project_folder, llm_kwargs, plugin
write_html(pfg.sp_file_contents, pfg.sp_file_result, chatbot=chatbot, project_folder=project_folder)
# <-------- 写出文件 ---------->
msg = f"当前大语言模型: {llm_kwargs['llm_model']},当前语言模型温度设定: {llm_kwargs['temperature']}"
final_tex = lps.merge_result(pfg.file_result, mode, msg)
@ -744,7 +751,6 @@ def 编译Latex(chatbot, history, main_file_original, main_file_modified, work_f
ok = compile_latex_with_timeout(f'latexdiff --encoding=utf8 --append-safecmd=subfile {work_folder_original}/{main_file_original}.tex {work_folder_modified}/{main_file_modified}.tex --flatten > {work_folder}/merge_diff.tex')
yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 正在编译对比PDF ...', chatbot, history) # 刷新Gradio前端界面
ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error merge_diff.tex', work_folder)
ok = compile_latex_with_timeout(f'bibtex merge_diff.aux', work_folder)
ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error merge_diff.tex', work_folder)
@ -769,7 +775,6 @@ def 编译Latex(chatbot, history, main_file_original, main_file_modified, work_f
result_pdf = pj(work_folder_modified, f'{main_file_modified}.pdf') # get pdf path
if os.path.exists(pj(work_folder, '..', 'translation')):
shutil.copyfile(result_pdf, pj(work_folder, '..', 'translation', 'translate_zh.pdf'))
promote_file_to_downloadzone(result_pdf, rename_file=None, chatbot=chatbot) # promote file to web UI
return True # 成功啦
else:

View File

@ -0,0 +1,89 @@
import time, threading, json
class AliyunASR():
def test_on_sentence_begin(self, message, *args):
# print("test_on_sentence_begin:{}".format(message))
pass
def test_on_sentence_end(self, message, *args):
# print("test_on_sentence_end:{}".format(message))
message = json.loads(message)
self.parsed_sentence = message['payload']['result']
self.event_on_entence_end.set()
print(self.parsed_sentence)
def test_on_start(self, message, *args):
# print("test_on_start:{}".format(message))
pass
def test_on_error(self, message, *args):
# print("on_error args=>{}".format(args))
pass
def test_on_close(self, *args):
# print("on_close: args=>{}".format(args))
pass
def test_on_result_chg(self, message, *args):
# print("test_on_chg:{}".format(message))
message = json.loads(message)
self.parsed_text = message['payload']['result']
self.event_on_result_chg.set()
def test_on_completed(self, message, *args):
# print("on_completed:args=>{} message=>{}".format(args, message))
pass
def audio_convertion_thread(self, uuid):
# 在一个异步线程中采集音频
import nls # pip install git+https://github.com/aliyun/alibabacloud-nls-python-sdk.git
import tempfile
from scipy import io
from toolbox import get_conf
from .audio_io import change_sample_rate
from .audio_io import RealtimeAudioDistribution
NEW_SAMPLERATE = 16000
rad = RealtimeAudioDistribution()
rad.clean_up()
temp_folder = tempfile.gettempdir()
TOKEN, APPKEY = get_conf('ALIYUN_TOKEN', 'ALIYUN_APPKEY')
URL="wss://nls-gateway.aliyuncs.com/ws/v1"
sr = nls.NlsSpeechTranscriber(
url=URL,
token=TOKEN,
appkey=APPKEY,
on_sentence_begin=self.test_on_sentence_begin,
on_sentence_end=self.test_on_sentence_end,
on_start=self.test_on_start,
on_result_changed=self.test_on_result_chg,
on_completed=self.test_on_completed,
on_error=self.test_on_error,
on_close=self.test_on_close,
callback_args=[uuid.hex]
)
r = sr.start(aformat="pcm",
enable_intermediate_result=True,
enable_punctuation_prediction=True,
enable_inverse_text_normalization=True)
while not self.stop:
# time.sleep(self.capture_interval)
audio = rad.read(uuid.hex)
if audio is not None:
# convert to pcm file
temp_file = f'{temp_folder}/{uuid.hex}.pcm' #
dsdata = change_sample_rate(audio, rad.rate, NEW_SAMPLERATE) # 48000 --> 16000
io.wavfile.write(temp_file, NEW_SAMPLERATE, dsdata)
# read pcm binary
with open(temp_file, "rb") as f: data = f.read()
# print('audio len:', len(audio), '\t ds len:', len(dsdata), '\t need n send:', len(data)//640)
slices = zip(*(iter(data),) * 640) # 640个字节为一组
for i in slices: sr.send_audio(bytes(i))
else:
time.sleep(0.1)
r = sr.stop()

View File

@ -0,0 +1,51 @@
import numpy as np
from scipy import interpolate
def Singleton(cls):
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return _singleton
@Singleton
class RealtimeAudioDistribution():
def __init__(self) -> None:
self.data = {}
self.max_len = 1024*1024
self.rate = 48000 # 只读,每秒采样数量
def clean_up(self):
self.data = {}
def feed(self, uuid, audio):
self.rate, audio_ = audio
# print('feed', len(audio_), audio_[-25:])
if uuid not in self.data:
self.data[uuid] = audio_
else:
new_arr = np.concatenate((self.data[uuid], audio_))
if len(new_arr) > self.max_len: new_arr = new_arr[-self.max_len:]
self.data[uuid] = new_arr
def read(self, uuid):
if uuid in self.data:
res = self.data.pop(uuid)
print('\r read-', len(res), '-', max(res), end='', flush=True)
else:
res = None
return res
def change_sample_rate(audio, old_sr, new_sr):
duration = audio.shape[0] / old_sr
time_old = np.linspace(0, duration, audio.shape[0])
time_new = np.linspace(0, duration, int(audio.shape[0] * new_sr / old_sr))
interpolator = interpolate.interp1d(time_old, audio.T)
new_audio = interpolator(time_new).T
return new_audio.astype(np.int16)

View File

@ -0,0 +1,63 @@
from toolbox import CatchException, update_ui
from .crazy_utils import request_gpt_model_in_new_thread_with_ui_alive
@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(("这是什么功能?", "交互功能函数模板。在执行完成之后, 可以将自身的状态存储到cookie中, 等待用户的再次调用。"))
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
state = chatbot._cookies.get('plugin_state_0001', None) # 初始化插件状态
if state is None:
chatbot._cookies['lock_plugin'] = 'crazy_functions.交互功能函数模板->交互功能模板函数' # 赋予插件锁定 锁定插件回调路径,当下一次用户提交时,会直接转到该函数
chatbot._cookies['plugin_state_0001'] = 'wait_user_keyword' # 赋予插件状态
chatbot.append(("第一次调用:", "请输入关键词, 我将为您查找相关壁纸, 建议使用英文单词, 插件锁定中,请直接提交即可。"))
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
return
if state == 'wait_user_keyword':
chatbot._cookies['lock_plugin'] = None # 解除插件锁定,避免遗忘导致死锁
chatbot._cookies['plugin_state_0001'] = None # 解除插件状态,避免遗忘导致死锁
# 解除插件锁定
chatbot.append((f"获取关键词:{txt}", ""))
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
page_return = get_image_page_by_keyword(txt)
inputs=inputs_show_user=f"Extract all image urls in this html page, pick the first 5 images and show them with markdown format: \n\n {page_return}"
gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(
inputs=inputs, inputs_show_user=inputs_show_user,
llm_kwargs=llm_kwargs, chatbot=chatbot, history=[],
sys_prompt="When you want to show an image, use markdown format. e.g. ![image_description](image_url). If there are no image url provided, answer 'no image url provided'"
)
chatbot[-1] = [chatbot[-1][0], gpt_say]
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
return
# ---------------------------------------------------------------------------------
def get_image_page_by_keyword(keyword):
import requests
from bs4 import BeautifulSoup
response = requests.get(f'https://wallhaven.cc/search?q={keyword}', timeout=2)
res = "image urls: \n"
for image_element in BeautifulSoup(response.content, 'html.parser').findAll("img"):
try:
res += image_element["data-src"]
res += "\n"
except:
pass
return res

View File

@ -27,22 +27,20 @@ def gen_image(llm_kwargs, prompt, resolution="256x256"):
}
response = requests.post(url, headers=headers, json=data, proxies=proxies)
print(response.content)
try:
image_url = json.loads(response.content.decode('utf8'))['data'][0]['url']
except:
raise RuntimeError(response.content.decode())
# 文件保存到本地
r = requests.get(image_url, proxies=proxies)
file_path = 'gpt_log/image_gen/'
os.makedirs(file_path, exist_ok=True)
file_name = 'Image' + time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + '.png'
with open(file_path + file_name, 'wb+') as f:
f.write(r.content)
return image_url, file_path + file_name
with open(file_path+file_name, 'wb+') as f: f.write(r.content)
return image_url, file_path+file_name
@CatchException

View File

@ -12,7 +12,7 @@ def write_chat_to_file(chatbot, history=None, file_name=None):
file_name = 'chatGPT对话历史' + time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + '.html'
os.makedirs('./gpt_log/', exist_ok=True)
with open(f'./gpt_log/{file_name}', 'w', encoding='utf8') as f:
from theme import advanced_css
from theme.theme import advanced_css
f.write(f'<!DOCTYPE html><head><meta charset="utf-8"><title>对话历史</title><style>{advanced_css}</style></head>')
for i, contents in enumerate(chatbot):
for j, content in enumerate(contents):

View File

@ -14,17 +14,19 @@ def 解析docx(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot
doc = Document(fp)
file_content = "\n".join([para.text for para in doc.paragraphs])
else:
import win32com.client
word = win32com.client.Dispatch("Word.Application")
word.visible = False
# 打开文件
print('fp', os.getcwd())
doc = word.Documents.Open(os.getcwd() + '/' + fp)
# file_content = doc.Content.Text
doc = word.ActiveDocument
file_content = doc.Range().Text
doc.Close()
word.Quit()
try:
import win32com.client
word = win32com.client.Dispatch("Word.Application")
word.visible = False
# 打开文件
doc = word.Documents.Open(os.getcwd() + '/' + fp)
# file_content = doc.Content.Text
doc = word.ActiveDocument
file_content = doc.Range().Text
doc.Close()
word.Quit()
except:
raise RuntimeError('请先将.doc文档转换为.docx文档。')
print(file_content)
# private_upload里面的文件名在解压zip后容易出现乱码rar和7z格式正常故可以只分析文章内容不输入文件名

View File

@ -1,121 +1,107 @@
from toolbox import update_ui
from toolbox import update_ui, promote_file_to_downloadzone, gen_time_str
from toolbox import CatchException, report_execption, write_results_to_file
import re
import unicodedata
fast_debug = False
from .crazy_utils import request_gpt_model_in_new_thread_with_ui_alive
from .crazy_utils import read_and_clean_pdf_text
from .crazy_utils import input_clipping
def is_paragraph_break(match):
"""
根据给定的匹配结果来判断换行符是否表示段落分隔。
如果换行符前为句子结束标志(句号,感叹号,问号),且下一个字符为大写字母,则换行符更有可能表示段落分隔。
也可以根据之前的内容长度来判断段落是否已经足够长。
"""
prev_char, next_char = match.groups()
# 句子结束标志
sentence_endings = ".!?"
# 设定一个最小段落长度阈值
min_paragraph_length = 140
if prev_char in sentence_endings and next_char.isupper() and len(match.string[:match.start(1)]) > min_paragraph_length:
return "\n\n"
else:
return " "
def normalize_text(text):
"""
通过把连字ligatures等文本特殊符号转换为其基本形式来对文本进行归一化处理。
例如,将连字 "fi" 转换为 "f""i"
"""
# 对文本进行归一化处理,分解连字
normalized_text = unicodedata.normalize("NFKD", text)
# 替换其他特殊字符
cleaned_text = re.sub(r'[^\x00-\x7F]+', '', normalized_text)
return cleaned_text
def clean_text(raw_text):
"""
对从 PDF 提取出的原始文本进行清洗和格式化处理。
1. 对原始文本进行归一化处理。
2. 替换跨行的连词
3. 根据 heuristic 规则判断换行符是否是段落分隔,并相应地进行替换
"""
# 对文本进行归一化处理
normalized_text = normalize_text(raw_text)
# 替换跨行的连词
text = re.sub(r'(\w+-\n\w+)', lambda m: m.group(1).replace('-\n', ''), normalized_text)
# 根据前后相邻字符的特点,找到原文本中的换行符
newlines = re.compile(r'(\S)\n(\S)')
# 根据 heuristic 规则,用空格或段落分隔符替换原换行符
final_text = re.sub(newlines, lambda m: m.group(1) + is_paragraph_break(m) + m.group(2), text)
return final_text.strip()
def 解析PDF(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt):
import time, glob, os, fitz
print('begin analysis on:', file_manifest)
for index, fp in enumerate(file_manifest):
with fitz.open(fp) as doc:
file_content = ""
for page in doc:
file_content += page.get_text()
file_content = clean_text(file_content)
print(file_content)
file_write_buffer = []
for file_name in file_manifest:
print('begin analysis on:', file_name)
############################## <第 0 步切割PDF> ##################################
# 递归地切割PDF文件每一块尽量是完整的一个section比如introductionexperiment等必要时再进行切割
# 的长度必须小于 2500 个 Token
file_content, page_one = read_and_clean_pdf_text(file_name) # 尝试按照章节切割PDF
file_content = file_content.encode('utf-8', 'ignore').decode() # avoid reading non-utf8 chars
page_one = str(page_one).encode('utf-8', 'ignore').decode() # avoid reading non-utf8 chars
TOKEN_LIMIT_PER_FRAGMENT = 2500
prefix = "接下来请你逐文件分析下面的论文文件,概括其内容" if index==0 else ""
i_say = prefix + f'请对下面的文章片段用中文做一个概述,文件名是{os.path.relpath(fp, project_folder)},文章内容是 ```{file_content}```'
i_say_show_user = prefix + f'[{index}/{len(file_manifest)}] 请对下面的文章片段做一个概述: {os.path.abspath(fp)}'
chatbot.append((i_say_show_user, "[Local Message] waiting gpt response."))
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
from .crazy_utils import breakdown_txt_to_satisfy_token_limit_for_pdf
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)
page_one_fragments = breakdown_txt_to_satisfy_token_limit_for_pdf(
txt=str(page_one), get_token_fn=get_token_num, limit=TOKEN_LIMIT_PER_FRAGMENT//4)
# 为了更好的效果我们剥离Introduction之后的部分如果有
paper_meta = page_one_fragments[0].split('introduction')[0].split('Introduction')[0].split('INTRODUCTION')[0]
############################## <第 1 步从摘要中提取高价值信息放到history中> ##################################
final_results = []
final_results.append(paper_meta)
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_show_user,
llm_kwargs=llm_kwargs,
chatbot=chatbot,
history=[],
sys_prompt="总结文章。"
) # 带超时倒计时
############################## <第 2 步,迭代地历遍整个文章,提取精炼信息> ##################################
i_say_show_user = f'首先你在中文语境下通读整篇论文。'; gpt_say = "[Local Message] 收到。" # 用户提示
chatbot.append([i_say_show_user, gpt_say]); yield from update_ui(chatbot=chatbot, history=[]) # 更新UI
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)
iteration_results = []
last_iteration_result = paper_meta # 初始值是摘要
MAX_WORD_TOTAL = 4096 * 0.7
n_fragment = len(paper_fragments)
if n_fragment >= 20: print('文章极长,不能达到预期效果')
for i in range(n_fragment):
NUM_OF_WORD = MAX_WORD_TOTAL // n_fragment
i_say = f"Read this section, recapitulate the content of this section with less than {NUM_OF_WORD} Chinese characters: {paper_fragments[i]}"
i_say_show_user = f"[{i+1}/{n_fragment}] Read this section, recapitulate the content of this section with less than {NUM_OF_WORD} Chinese characters: {paper_fragments[i][:200]}"
gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(i_say, i_say_show_user, # i_say=真正给chatgpt的提问 i_say_show_user=给用户看的提问
llm_kwargs, chatbot,
history=["The main idea of the previous section is?", last_iteration_result], # 迭代上一次的结果
sys_prompt="Extract the main idea of this section with Chinese." # 提示
)
iteration_results.append(gpt_say)
last_iteration_result = gpt_say
all_file = ', '.join([os.path.relpath(fp, project_folder) for index, fp in enumerate(file_manifest)])
i_say = f'根据以上你自己的分析,对全文进行概括,用学术性语言写一段中文摘要,然后再写一段英文摘要(包括{all_file})。'
chatbot.append((i_say, "[Local Message] waiting gpt response."))
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
if not fast_debug:
msg = '正常'
# ** gpt request **
############################## <第 3 步整理history提取总结> ##################################
final_results.extend(iteration_results)
final_results.append(f'Please conclude this paper discussed above。')
# This prompt is from https://github.com/kaixindelele/ChatPaper/blob/main/chat_paper.py
NUM_OF_WORD = 1000
i_say = """
1. Mark the title of the paper (with Chinese translation)
2. list all the authors' names (use English)
3. mark the first author's affiliation (output Chinese translation only)
4. mark the keywords of this article (use English)
5. link to the paper, Github code link (if available, fill in Github:None if not)
6. summarize according to the following four points.Be sure to use Chinese answers (proper nouns need to be marked in English)
- (1):What is the research background of this article?
- (2):What are the past methods? What are the problems with them? Is the approach well motivated?
- (3):What is the research methodology proposed in this paper?
- (4):On what task and what performance is achieved by the methods in this paper? Can the performance support their goals?
Follow the format of the output that follows:
1. Title: xxx\n\n
2. Authors: xxx\n\n
3. Affiliation: xxx\n\n
4. Keywords: xxx\n\n
5. Urls: xxx or xxx , xxx \n\n
6. Summary: \n\n
- (1):xxx;\n
- (2):xxx;\n
- (3):xxx;\n
- (4):xxx.\n\n
Be sure to use Chinese answers (proper nouns need to be marked in English), statements as concise and academic as possible,
do not have too much repetitive information, numerical values using the original numbers.
"""
# This prompt is from https://github.com/kaixindelele/ChatPaper/blob/main/chat_paper.py
file_write_buffer.extend(final_results)
i_say, final_results = input_clipping(i_say, final_results, max_token_limit=2000)
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="总结文章。"
) # 带超时倒计时
inputs=i_say, inputs_show_user='开始最终总结',
llm_kwargs=llm_kwargs, chatbot=chatbot, history=final_results,
sys_prompt= f"Extract the main idea of this paper with less than {NUM_OF_WORD} Chinese characters"
)
final_results.append(gpt_say)
file_write_buffer.extend([i_say, gpt_say])
############################## <第 4 步设置一个token上限> ##################################
_, final_results = input_clipping("", final_results, max_token_limit=3200)
yield from update_ui(chatbot=chatbot, history=final_results) # 注意这里的历史记录被替代了
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) # 刷新界面
res = write_results_to_file(file_write_buffer, file_name=gen_time_str())
promote_file_to_downloadzone(res.split('\t')[-1], chatbot=chatbot)
yield from update_ui(chatbot=chatbot, history=final_results) # 刷新界面
@CatchException
@ -151,10 +137,7 @@ def 批量总结PDF文档(txt, llm_kwargs, plugin_kwargs, chatbot, history, syst
return
# 搜索需要处理的文件清单
file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.pdf', 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)]
file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.pdf', recursive=True)]
# 如果没找到任何文件
if len(file_manifest) == 0:

View File

@ -53,10 +53,9 @@ def 解析PDF(file_name, llm_kwargs, plugin_kwargs, chatbot, history, system_pro
)
iteration_results.append(gpt_say)
last_iteration_result = gpt_say
############################## <第 3 步整理history> ##################################
final_results.extend(iteration_results)
# 将摘要添加到历史中,方便"猜你想问"使用
history.extend([last_iteration_result])
final_results.append(f'接下来,你是一名专业的学术教授,利用以上信息,使用中文回答我的问题。')
# 接下来两句话只显示在界面上,不起实际作用
i_say_show_user = f'接下来,你是一名专业的学术教授,利用以上信息,使用中文回答我的问题。'; gpt_say = "[Local Message] 收到。"
@ -113,4 +112,3 @@ def 理解PDF文档内容标准文件输入(txt, llm_kwargs, plugin_kwargs, chat
txt = file_manifest[0]
# 开始正式执行任务
yield from 解析PDF(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt)

View File

@ -144,13 +144,3 @@ def 解析ipynb文件(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_p
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
return
yield from ipynb解释(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, )
if __name__ == '__main__':
import json
filename = ''
code = parseNotebook(filename)
print(code)
with open(filename, 'r', encoding='utf-8', errors='replace') as f:
notebook = f.read()
print(notebook)

View File

@ -6,7 +6,7 @@ def 同时问询(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt
"""
txt 输入栏用户输入的文本,例如需要翻译的一段话,再例如一个包含了待处理文件的路径
llm_kwargs gpt模型参数如温度和top_p等一般原样传递下去就行
plugin_kwargs 插件模型的参数,如温度和top_p等一般原样传递下去就行
plugin_kwargs 插件模型的参数,用于灵活调整复杂功能的各种参数
chatbot 聊天显示框的句柄,用于显示给用户
history 聊天历史,前情提要
system_prompt 给gpt的静默提醒
@ -35,7 +35,7 @@ def 同时问询_指定模型(txt, llm_kwargs, plugin_kwargs, chatbot, history,
"""
txt 输入栏用户输入的文本,例如需要翻译的一段话,再例如一个包含了待处理文件的路径
llm_kwargs gpt模型参数如温度和top_p等一般原样传递下去就行
plugin_kwargs 插件模型的参数,如温度和top_p等一般原样传递下去就行
plugin_kwargs 插件模型的参数,用于灵活调整复杂功能的各种参数
chatbot 聊天显示框的句柄,用于显示给用户
history 聊天历史,前情提要
system_prompt 给gpt的静默提醒

View File

@ -0,0 +1,188 @@
from toolbox import update_ui
from toolbox import CatchException, get_conf, markdown_convertion
from crazy_functions.crazy_utils import input_clipping
from request_llm.bridge_all import predict_no_ui_long_connection
import threading, time
import numpy as np
from .live_audio.aliyunASR import AliyunASR
import json
class WatchDog():
def __init__(self, timeout, bark_fn, interval=3, msg="") -> None:
self.last_feed = None
self.timeout = timeout
self.bark_fn = bark_fn
self.interval = interval
self.msg = msg
def watch(self):
while True:
if time.time() - self.last_feed > self.timeout:
if len(self.msg) > 0: print(self.msg)
self.bark_fn()
break
time.sleep(self.interval)
def begin_watch(self):
self.last_feed = time.time()
th = threading.Thread(target=self.watch)
th.daemon = True
th.start()
def feed(self):
self.last_feed = time.time()
def chatbot2history(chatbot):
history = []
for c in chatbot:
for q in c:
if q not in ["[请讲话]", "[等待GPT响应]", "[正在等您说完问题]"]:
history.append(q.strip('<div class="markdown-body">').strip('</div>').strip('<p>').strip('</p>'))
return history
class AsyncGptTask():
def __init__(self) -> None:
self.observe_future = []
self.observe_future_chatbot_index = []
def gpt_thread_worker(self, i_say, llm_kwargs, history, sys_prompt, observe_window, index):
try:
MAX_TOKEN_ALLO = 2560
i_say, history = input_clipping(i_say, history, max_token_limit=MAX_TOKEN_ALLO)
gpt_say_partial = predict_no_ui_long_connection(inputs=i_say, llm_kwargs=llm_kwargs, history=history, sys_prompt=sys_prompt,
observe_window=observe_window[index], console_slience=True)
except ConnectionAbortedError as token_exceed_err:
print('至少一个线程任务Token溢出而失败', e)
except Exception as e:
print('至少一个线程任务意外失败', e)
def add_async_gpt_task(self, i_say, chatbot_index, llm_kwargs, history, system_prompt):
self.observe_future.append([""])
self.observe_future_chatbot_index.append(chatbot_index)
cur_index = len(self.observe_future)-1
th_new = threading.Thread(target=self.gpt_thread_worker, args=(i_say, llm_kwargs, history, system_prompt, self.observe_future, cur_index))
th_new.daemon = True
th_new.start()
def update_chatbot(self, chatbot):
for of, ofci in zip(self.observe_future, self.observe_future_chatbot_index):
try:
chatbot[ofci] = list(chatbot[ofci])
chatbot[ofci][1] = markdown_convertion(of[0])
except:
self.observe_future = []
self.observe_future_chatbot_index = []
return chatbot
class InterviewAssistant(AliyunASR):
def __init__(self):
self.capture_interval = 0.5 # second
self.stop = False
self.parsed_text = ""
self.parsed_sentence = ""
self.buffered_sentence = ""
self.event_on_result_chg = threading.Event()
self.event_on_entence_end = threading.Event()
self.event_on_commit_question = threading.Event()
def __del__(self):
self.stop = True
def init(self, chatbot):
# 初始化音频采集线程
self.captured_audio = np.array([])
self.keep_latest_n_second = 10
self.commit_after_pause_n_second = 1.5
self.ready_audio_flagment = None
self.stop = False
self.plugin_wd = WatchDog(timeout=5, bark_fn=self.__del__, msg="程序终止")
self.aut = threading.Thread(target=self.audio_convertion_thread, args=(chatbot._cookies['uuid'],))
self.aut.daemon = True
self.aut.start()
# th2 = threading.Thread(target=self.audio2txt_thread, args=(chatbot._cookies['uuid'],))
# th2.daemon = True
# th2.start()
def no_audio_for_a_while(self):
if len(self.buffered_sentence) < 7: # 如果一句话小于7个字暂不提交
self.commit_wd.begin_watch()
else:
self.event_on_commit_question.set()
def begin(self, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt):
# main plugin function
self.init(chatbot)
chatbot.append(["[请讲话]", "[正在等您说完问题]"])
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
self.plugin_wd.begin_watch()
self.agt = AsyncGptTask()
self.commit_wd = WatchDog(timeout=self.commit_after_pause_n_second, bark_fn=self.no_audio_for_a_while, interval=0.2)
self.commit_wd.begin_watch()
while True:
self.event_on_result_chg.wait(timeout=0.25) # run once every 0.25 second
chatbot = self.agt.update_chatbot(chatbot) # 将子线程的gpt结果写入chatbot
history = chatbot2history(chatbot)
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
self.plugin_wd.feed()
if self.event_on_result_chg.is_set():
# update audio decode result
self.event_on_result_chg.clear()
chatbot[-1] = list(chatbot[-1])
chatbot[-1][0] = self.buffered_sentence + self.parsed_text
history = chatbot2history(chatbot)
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
self.commit_wd.feed()
if self.event_on_entence_end.is_set():
# called when a sentence has ended
self.event_on_entence_end.clear()
self.parsed_text = self.parsed_sentence
self.buffered_sentence += self.parsed_sentence
if self.event_on_commit_question.is_set():
# called when a question should be commited
self.event_on_commit_question.clear()
if len(self.buffered_sentence) == 0: raise RuntimeError
self.commit_wd.begin_watch()
chatbot[-1] = list(chatbot[-1])
chatbot[-1] = [self.buffered_sentence, "[等待GPT响应]"]
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
# add gpt task 创建子线程请求gpt避免线程阻塞
history = chatbot2history(chatbot)
self.agt.add_async_gpt_task(self.buffered_sentence, len(chatbot)-1, llm_kwargs, history, system_prompt)
self.buffered_sentence = ""
chatbot.append(["[请讲话]", "[正在等您说完问题]"])
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
@CatchException
def 语音助手(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
# pip install -U openai-whisper
chatbot.append(["对话助手函数插件:使用时,双手离开鼠标键盘吧", "音频助手, 正在听您讲话(点击“停止”键可终止程序)..."])
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
# 尝试导入依赖,如果缺少依赖,则给出安装建议
try:
import nls
from scipy import io
except:
chatbot.append(["导入依赖失败", "使用该模块需要额外依赖, 安装方法:```pip install --upgrade pyOpenSSL scipy git+https://github.com/aliyun/alibabacloud-nls-python-sdk.git```"])
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
return
TOKEN, APPKEY = get_conf('ALIYUN_TOKEN', 'ALIYUN_APPKEY')
if TOKEN == "" or APPKEY == "":
chatbot.append(["导入依赖失败", "没有阿里云语音识别APPKEY和TOKEN, 详情见https://help.aliyun.com/document_detail/450255.html"])
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
return
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
ia = InterviewAssistant()
yield from ia.begin(llm_kwargs, plugin_kwargs, chatbot, history, system_prompt)

View File

@ -13,13 +13,8 @@ def 猜你想问(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt
show_say = txt
prompt = txt+'\n回答完问题后,再列出用户可能提出的三个问题。'
else:
prompt = history[-1]+"\n分析上述回答,再列出用户可能提出的三个问题。"
show_say = '分析上述回答,再列出用户可能提出的三个问题。'
try:
prompt = history[-1]+f"\n{show_say}"
except IndexError:
prompt = system_prompt+"\n再列出用户可能提出的三个问题。"
gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(
inputs=prompt,
inputs_show_user=show_say,
@ -28,8 +23,6 @@ def 猜你想问(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt
history=history,
sys_prompt=system_prompt
)
chatbot[-1] = (show_say, gpt_say)
history.extend([show_say, gpt_say])
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面

View File

@ -1,13 +1,12 @@
from toolbox import CatchException, update_ui
from .crazy_utils import request_gpt_model_in_new_thread_with_ui_alive
import datetime, re
import datetime
@CatchException
def 高阶功能模板函数(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
"""
txt 输入栏用户输入的文本,例如需要翻译的一段话,再例如一个包含了待处理文件的路径
llm_kwargs gpt模型参数如温度和top_p等一般原样传递下去就行
plugin_kwargs 插件模型的参数,暂时没有用武之地
plugin_kwargs 插件模型的参数,用于灵活调整复杂功能的各种参数
chatbot 聊天显示框的句柄,用于显示给用户
history 聊天历史,前情提要
system_prompt 给gpt的静默提醒
@ -19,34 +18,12 @@ def 高阶功能模板函数(txt, llm_kwargs, plugin_kwargs, chatbot, history, s
for i in range(5):
currentMonth = (datetime.date.today() + datetime.timedelta(days=i)).month
currentDay = (datetime.date.today() + datetime.timedelta(days=i)).day
i_say = f'历史中哪些事件发生在{currentMonth}{currentDay}日?用中文列举两条,然后分别给出描述事件的两个英文单词。' + '当你给出关键词时,使用以下json格式{"KeyWords":[EnglishKeyWord1,EnglishKeyWord2]}'
i_say = f'历史中哪些事件发生在{currentMonth}{currentDay}日?列举两条并发送相关图片。发送图片时,使用Markdown将Unsplash API中的PUT_YOUR_QUERY_HERE替换成描述该事件的一个最重要的单词'
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=[],
sys_prompt='输出格式示例1908年美国消防救援事业发展的“美国消防协会”成立。关键词{"KeyWords":["Fire","American"]}。'
sys_prompt="当你想发送一张照片时请使用Markdown, 并且不要有反斜线, 不要用代码块。使用 Unsplash API (https://source.unsplash.com/1280x720/? < PUT_YOUR_QUERY_HERE >)。"
)
gpt_say = get_images(gpt_say)
chatbot[-1] = (i_say, gpt_say)
history.append(i_say);history.append(gpt_say)
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 界面更新
def get_images(gpt_say):
def get_image_by_keyword(keyword):
import requests
from bs4 import BeautifulSoup
response = requests.get(f'https://wallhaven.cc/search?q={keyword}', timeout=2)
for image_element in BeautifulSoup(response.content, 'html.parser').findAll("img"):
if "data-src" in image_element: break
return image_element["data-src"]
for keywords in re.findall('{"KeyWords":\[(.*?)\]}', gpt_say):
keywords = [n.strip('"') for n in keywords.split(',')]
try:
description = keywords[0]
url = get_image_by_keyword(keywords[0])
img_tag = f"\n\n![{description}]({url})"
gpt_say += img_tag
except:
continue
return gpt_say
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 界面更新

View File

@ -6,7 +6,7 @@
version: '3'
services:
gpt_academic_nolocalllms:
image: ghcr.io/binary-husky/gpt_academic_nolocal:master
image: ghcr.io/binary-husky/gpt_academic_nolocal:master # (Auto Built by Dockerfile: docs/GithubAction+NoLocal)
environment:
# 请查阅 `config.py` 以查看所有的配置信息
API_KEY: ' sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx '
@ -33,7 +33,7 @@ services:
version: '3'
services:
gpt_academic_with_chatglm:
image: ghcr.io/binary-husky/gpt_academic_chatglm_moss:master
image: ghcr.io/binary-husky/gpt_academic_chatglm_moss:master # (Auto Built by Dockerfile: docs/Dockerfile+ChatGLM)
environment:
# 请查阅 `config.py` 以查看所有的配置信息
API_KEY: ' sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,fkxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx '
@ -63,7 +63,7 @@ services:
version: '3'
services:
gpt_academic_with_rwkv:
image: fuqingxu/gpt_academic:jittorllms # [option 2] 如果需要运行ChatGLM本地模型
image: fuqingxu/gpt_academic:jittorllms
environment:
# 请查阅 `config.py` 以查看所有的配置信息
API_KEY: ' sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,fkxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx '
@ -111,7 +111,7 @@ services:
version: '3'
services:
gpt_academic_with_latex:
image: ghcr.io/binary-husky/gpt_academic_with_latex:master
image: ghcr.io/binary-husky/gpt_academic_with_latex:master # (Auto Built by Dockerfile: docs/GithubAction+NoLocal+Latex)
environment:
# 请查阅 `config.py` 以查看所有的配置信息
API_KEY: ' sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx '

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

View File

@ -1,465 +0,0 @@
// custom javascript here
const MAX_HISTORY_LENGTH = 32;
var key_down_history = [];
var currentIndex = -1;
var user_input_ta;
var gradioContainer = null;
var user_input_ta = null;
var chat_txt = null;
var userInfoDiv = null;
var appTitleDiv = null;
var chatbot = null;
var chatbotWrap = null;
var apSwitch = null;
var messageBotDivs = null;
var loginUserForm = null;
var logginUser = null;
var userLogged = false;
var usernameGotten = false;
var historyLoaded = false;
var ga = document.getElementsByTagName("gradio-app");
var targetNode = ga[0];
var isInIframe = (window.self !== window.top);
var language = navigator.language.slice(0,2);
var forView_i18n = {
'zh': "仅供查看",
'en': "For viewing only",
'ja': "閲覧専用",
'fr': "Pour consultation seulement",
'es': "Solo para visualización",
};
var deleteConfirm_i18n_pref = {
'zh': "你真的要删除 ",
'en': "Are you sure you want to delete ",
'ja': "本当に ",
};
var deleteConfirm_i18n_suff = {
'zh': " 吗?",
'en': " ?",
'ja': " を削除してもよろしいですか?",
};
var deleteConfirm_msg_pref = "Are you sure you want to delete ";
var deleteConfirm_msg_suff = " ?";
// gradio 页面加载好了么??? 我能动你的元素了么??
function gradioLoaded(mutations) {
for (var i = 0; i < mutations.length; i++) {
if (mutations[i].addedNodes.length) {
loginUserForm = document.querySelector(".gradio-container > .main > .wrap > .panel > .form")
gradioContainer = document.querySelector(".gradio-container");
chat_txt = document.getElementById('chat_txt');
userInfoDiv = document.getElementById("user_info");
appTitleDiv = document.getElementById("app_title");
chatbot = document.querySelector('#废弃');
chatbotWrap = document.querySelector('#废弃 > .wrap');
apSwitch = document.querySelector('.apSwitch input[type="checkbox"]');
if (loginUserForm) {
localStorage.setItem("userLogged", true);
userLogged = true;
}
if (gradioContainer && apSwitch) { // gradioCainter 加载出来了没?
adjustDarkMode();
}
if (chat_txt) { // chat_txt 加载出来了没?
selectHistory();
}
if (userInfoDiv && appTitleDiv) { // userInfoDiv 和 appTitleDiv 加载出来了没?
if (!usernameGotten) {
getUserInfo();
}
setTimeout(showOrHideUserInfo(), 2000);
}
if (chatbot) { // chatbot 加载出来了没?
setChatbotHeight();
}
if (chatbotWrap) {
if (!historyLoaded) {
loadHistoryHtml();
}
setChatbotScroll();
}
}
}
}
function webLocale() {
// console.log("webLocale", language);
if (forView_i18n.hasOwnProperty(language)) {
var forView = forView_i18n[language];
var forViewStyle = document.createElement('style');
forViewStyle.innerHTML = '.wrap>.history-message>:last-child::after { content: "' + forView + '"!important; }';
document.head.appendChild(forViewStyle);
}
if (deleteConfirm_i18n_pref.hasOwnProperty(language)) {
deleteConfirm_msg_pref = deleteConfirm_i18n_pref[language];
deleteConfirm_msg_suff = deleteConfirm_i18n_suff[language];
}
}
function showConfirmationDialog(a, file, c) {
if (file != "") {
var result = confirm(deleteConfirm_msg_pref + file + deleteConfirm_msg_suff);
if (result) {
return [a, file, c];
}
}
return [a, "CANCELED", c];
}
function selectHistory() {
user_input_ta = chat_txt.querySelector("textarea");
if (user_input_ta) {
observer.disconnect(); // 停止监听
// 在 textarea 上监听 keydown 事件
user_input_ta.addEventListener("keydown", function (event) {
var value = user_input_ta.value.trim();
// 判断按下的是否为方向键
if (event.code === 'ArrowUp' || event.code === 'ArrowDown') {
// 如果按下的是方向键,且输入框中有内容,且历史记录中没有该内容,则不执行操作
if (value && key_down_history.indexOf(value) === -1)
return;
// 对于需要响应的动作,阻止默认行为。
event.preventDefault();
var length = key_down_history.length;
if (length === 0) {
currentIndex = -1; // 如果历史记录为空,直接将当前选中的记录重置
return;
}
if (currentIndex === -1) {
currentIndex = length;
}
if (event.code === 'ArrowUp' && currentIndex > 0) {
currentIndex--;
user_input_ta.value = key_down_history[currentIndex];
} else if (event.code === 'ArrowDown' && currentIndex < length - 1) {
currentIndex++;
user_input_ta.value = key_down_history[currentIndex];
}
user_input_ta.selectionStart = user_input_ta.value.length;
user_input_ta.selectionEnd = user_input_ta.value.length;
const input_event = new InputEvent("input", { bubbles: true, cancelable: true });
user_input_ta.dispatchEvent(input_event);
} else if (event.code === "Enter") {
if (value) {
currentIndex = -1;
if (key_down_history.indexOf(value) === -1) {
key_down_history.push(value);
if (key_down_history.length > MAX_HISTORY_LENGTH) {
key_down_history.shift();
}
}
}
}
});
}
}
var username = null;
function getUserInfo() {
if (usernameGotten) {
return;
}
userLogged = localStorage.getItem('userLogged');
if (userLogged) {
username = userInfoDiv.innerText;
if (username) {
if (username.includes("getting user info…")) {
setTimeout(getUserInfo, 500);
return;
} else if (username === " ") {
localStorage.removeItem("username");
localStorage.removeItem("userLogged")
userLogged = false;
usernameGotten = true;
return;
} else {
username = username.match(/User:\s*(.*)/)[1] || username;
localStorage.setItem("username", username);
usernameGotten = true;
clearHistoryHtml();
}
}
}
}
function toggleUserInfoVisibility(shouldHide) {
if (userInfoDiv) {
if (shouldHide) {
userInfoDiv.classList.add("hideK");
} else {
userInfoDiv.classList.remove("hideK");
}
}
}
function showOrHideUserInfo() {
var sendBtn = document.getElementById("submit_btn");
// Bind mouse/touch events to show/hide user info
appTitleDiv.addEventListener("mouseenter", function () {
toggleUserInfoVisibility(false);
});
userInfoDiv.addEventListener("mouseenter", function () {
toggleUserInfoVisibility(false);
});
sendBtn.addEventListener("mouseenter", function () {
toggleUserInfoVisibility(false);
});
appTitleDiv.addEventListener("mouseleave", function () {
toggleUserInfoVisibility(true);
});
userInfoDiv.addEventListener("mouseleave", function () {
toggleUserInfoVisibility(true);
});
sendBtn.addEventListener("mouseleave", function () {
toggleUserInfoVisibility(true);
});
appTitleDiv.ontouchstart = function () {
toggleUserInfoVisibility(false);
};
userInfoDiv.ontouchstart = function () {
toggleUserInfoVisibility(false);
};
sendBtn.ontouchstart = function () {
toggleUserInfoVisibility(false);
};
appTitleDiv.ontouchend = function () {
setTimeout(function () {
toggleUserInfoVisibility(true);
}, 3000);
};
userInfoDiv.ontouchend = function () {
setTimeout(function () {
toggleUserInfoVisibility(true);
}, 3000);
};
sendBtn.ontouchend = function () {
setTimeout(function () {
toggleUserInfoVisibility(true);
}, 3000); // Delay 1 second to hide user info
};
// Hide user info after 2 second
setTimeout(function () {
toggleUserInfoVisibility(true);
}, 2000);
}
function toggleDarkMode(isEnabled) {
if (isEnabled) {
document.body.classList.add("dark");
document.body.style.setProperty("background-color", "var(--neutral-950)", "important");
} else {
document.body.classList.remove("dark");
document.body.style.backgroundColor = "";
}
}
function adjustDarkMode() {
const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
// 根据当前颜色模式设置初始状态
apSwitch.checked = darkModeQuery.matches;
toggleDarkMode(darkModeQuery.matches);
// 监听颜色模式变化
darkModeQuery.addEventListener("change", (e) => {
apSwitch.checked = e.matches;
toggleDarkMode(e.matches);
});
// apSwitch = document.querySelector('.apSwitch input[type="checkbox"]');
apSwitch.addEventListener("change", (e) => {
toggleDarkMode(e.target.checked);
});
}
function setChatbotHeight() {
const screenWidth = window.innerWidth;
const statusDisplay = document.querySelector('#status_display');
const statusDisplayHeight = statusDisplay ? statusDisplay.offsetHeight : 0;
const wrap = chatbot.querySelector('.wrap');
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
if (isInIframe) {
chatbot.style.height = `700px`;
wrap.style.maxHeight = `calc(700px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`
} else {
if (screenWidth <= 320) {
chatbot.style.height = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 150}px)`;
wrap.style.maxHeight = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 150}px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`;
} else if (screenWidth <= 499) {
chatbot.style.height = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 100}px)`;
wrap.style.maxHeight = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 100}px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`;
} else {
chatbot.style.height = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 160}px)`;
wrap.style.maxHeight = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 160}px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`;
}
}
}
function setChatbotScroll() {
var scrollHeight = chatbotWrap.scrollHeight;
chatbotWrap.scrollTo(0,scrollHeight)
}
var rangeInputs = null;
var numberInputs = null;
function setSlider() {
rangeInputs = document.querySelectorAll('input[type="range"]');
numberInputs = document.querySelectorAll('input[type="number"]')
setSliderRange();
rangeInputs.forEach(rangeInput => {
rangeInput.addEventListener('input', setSliderRange);
});
numberInputs.forEach(numberInput => {
numberInput.addEventListener('input', setSliderRange);
})
}
function setSliderRange() {
var range = document.querySelectorAll('input[type="range"]');
range.forEach(range => {
range.style.backgroundSize = (range.value - range.min) / (range.max - range.min) * 100 + '% 100%';
});
}
function addChuanhuButton(botElement) {
var rawMessage = null;
var mdMessage = null;
rawMessage = botElement.querySelector('.raw-message');
mdMessage = botElement.querySelector('.md-message');
if (!rawMessage) {
var buttons = botElement.querySelectorAll('button.chuanhu-btn');
for (var i = 0; i < buttons.length; i++) {
buttons[i].parentNode.removeChild(buttons[i]);
}
return;
}
var copyButton = null;
var toggleButton = null;
copyButton = botElement.querySelector('button.copy-bot-btn');
toggleButton = botElement.querySelector('button.toggle-md-btn');
if (copyButton) copyButton.remove();
if (toggleButton) toggleButton.remove();
// Copy bot button
var copyButton = document.createElement('button');
copyButton.classList.add('chuanhu-btn');
copyButton.classList.add('copy-bot-btn');
copyButton.setAttribute('aria-label', 'Copy');
copyButton.innerHTML = copyIcon;
copyButton.addEventListener('click', () => {
const textToCopy = rawMessage.innerText;
navigator.clipboard
.writeText(textToCopy)
.then(() => {
copyButton.innerHTML = copiedIcon;
setTimeout(() => {
copyButton.innerHTML = copyIcon;
}, 1500);
})
.catch(() => {
console.error("copy failed");
});
});
botElement.appendChild(copyButton);
// Toggle button
var toggleButton = document.createElement('button');
toggleButton.classList.add('chuanhu-btn');
toggleButton.classList.add('toggle-md-btn');
toggleButton.setAttribute('aria-label', 'Toggle');
var renderMarkdown = mdMessage.classList.contains('hideM');
toggleButton.innerHTML = renderMarkdown ? mdIcon : rawIcon;
toggleButton.addEventListener('click', () => {
renderMarkdown = mdMessage.classList.contains('hideM');
if (renderMarkdown){
renderMarkdownText(botElement);
toggleButton.innerHTML=rawIcon;
} else {
removeMarkdownText(botElement);
toggleButton.innerHTML=mdIcon;
}
});
botElement.insertBefore(toggleButton, copyButton);
}
function renderMarkdownText(message) {
var mdDiv = message.querySelector('.md-message');
if (mdDiv) mdDiv.classList.remove('hideM');
var rawDiv = message.querySelector('.raw-message');
if (rawDiv) rawDiv.classList.add('hideM');
}
function removeMarkdownText(message) {
var rawDiv = message.querySelector('.raw-message');
if (rawDiv) rawDiv.classList.remove('hideM');
var mdDiv = message.querySelector('.md-message');
if (mdDiv) mdDiv.classList.add('hideM');
}
let timeoutId;
let isThrottled = false;
var mmutation
// 监听所有元素中 bot message 的变化,为 bot 消息添加复制按钮。
var mObserver = new MutationObserver(function (mutationsList) {
for (mmutation of mutationsList) {
if (mmutation.type === 'childList') {
for (var node of mmutation.addedNodes) {
if (node.nodeType === 1 && node.classList.contains('message') && node.getAttribute('data-testid') === 'bot') {
saveHistoryHtml();
document.querySelectorAll('#废弃>.wrap>.message-wrap .message.bot').forEach(addChuanhuButton);
}
if (node.tagName === 'INPUT' && node.getAttribute('type') === 'range') {
setSlider();
}
}
for (var node of mmutation.removedNodes) {
if (node.nodeType === 1 && node.classList.contains('message') && node.getAttribute('data-testid') === 'bot') {
saveHistoryHtml();
document.querySelectorAll('#废弃>.wrap>.message-wrap .message.bot').forEach(addChuanhuButton);
}
}
} else if (mmutation.type === 'attributes') {
if (mmutation.target.nodeType === 1 && mmutation.target.classList.contains('message') && mmutation.target.getAttribute('data-testid') === 'bot') {
if (isThrottled) break; // 为了防止重复不断疯狂渲染加上等待_(:з」∠)_
isThrottled = true;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
isThrottled = false;
document.querySelectorAll('#废弃>.wrap>.message-wrap .message.bot').forEach(addChuanhuButton);
saveHistoryHtml();
}, 500);
}
}
}
});
mObserver.observe(document.documentElement, { attributes: true, childList: true, subtree: true });
// 监视页面内部 DOM 变动
var observer = new MutationObserver(function (mutations) {
gradioLoaded(mutations);
});
observer.observe(targetNode, { childList: true, subtree: true });
// 监视页面变化
window.addEventListener("DOMContentLoaded", function () {
isInIframe = (window.self !== window.top);
historyLoaded = false;
});
window.addEventListener('resize', setChatbotHeight);
window.addEventListener('scroll', setChatbotHeight);
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", adjustDarkMode);
// button svg code
const copyIcon = '<span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg></span>';
const copiedIcon = '<span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><polyline points="20 6 9 17 4 12"></polyline></svg></span>';
const mdIcon = '<span><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 14 18" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><g transform-origin="center" transform="scale(0.85)"><path d="M1.5,0 L12.5,0 C13.3284271,-1.52179594e-16 14,0.671572875 14,1.5 L14,16.5 C14,17.3284271 13.3284271,18 12.5,18 L1.5,18 C0.671572875,18 1.01453063e-16,17.3284271 0,16.5 L0,1.5 C-1.01453063e-16,0.671572875 0.671572875,1.52179594e-16 1.5,0 Z" stroke-width="1.8"></path><line x1="3.5" y1="3.5" x2="10.5" y2="3.5"></line><line x1="3.5" y1="6.5" x2="8" y2="6.5"></line></g><path d="M4,9 L10,9 C10.5522847,9 11,9.44771525 11,10 L11,13.5 C11,14.0522847 10.5522847,14.5 10,14.5 L4,14.5 C3.44771525,14.5 3,14.0522847 3,13.5 L3,10 C3,9.44771525 3.44771525,9 4,9 Z" stroke="none" fill="currentColor"></path></svg></span>';
const rawIcon = '<span><svg stroke="currentColor" fill="none" stroke-width="1.8" viewBox="0 0 18 14" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><g transform-origin="center" transform="scale(0.85)"><polyline points="4 3 0 7 4 11"></polyline><polyline points="14 3 18 7 14 11"></polyline><line x1="12" y1="0" x2="6" y2="14"></line></g></svg></span>';

View File

@ -1,2 +0,0 @@
// external javascript here

View File

@ -1,8 +0,0 @@
<div style="display: flex; justify-content: space-between;">
<span>
<label class="apSwitch" for="checkbox">
<input type="checkbox" id="checkbox">
<div class="apSlider"></div>
</label>
</span>
</div>

View File

@ -1,9 +0,0 @@
<b>{label}</b>
<div class="progress-bar">
<div class="progress" style="width: {usage_percent}%;">
<span class="progress-text">{usage_percent}%</span>
</div>
</div>
<div style="display: flex; justify-content: space-between;">
<span>${rounded_usage}</span><span>${usage_limit}</span>
</div>

View File

@ -1 +0,0 @@
<div class="versions">{versions}</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -49,7 +49,7 @@ def markdown_convertion(txt):
"""
将Markdown格式的文本转换为HTML格式。如果包含数学公式则先将公式转换为HTML格式。
"""
pre = '<div class="md-message">'
pre = '<div class="markdown-body">'
suf = '</div>'
if txt.startswith(pre) and txt.endswith(suf):
# print('警告,输入了已经经过转化的字符串,二次转化可能出问题')

View File

@ -265,7 +265,7 @@
"例如chatglm&gpt-3.5-turbo&api2d-gpt-4": "e.g. chatglm&gpt-3.5-turbo&api2d-gpt-4",
"先切换模型到openai或api2d": "Switch the model to openai or api2d first",
"在这里输入分辨率": "Enter the resolution here",
"如'256x256', '512x512', '1024x1024'": "e.g. '256x256', '512x512', '1024x1024'",
"如256x256": "e.g. 256x256",
"默认": "Default",
"建议您复制一个config_private.py放自己的秘密": "We suggest you to copy a config_private.py file to keep your secrets, such as API and proxy URLs, from being accidentally uploaded to Github and seen by others.",
"如API和代理网址": "Such as API and proxy URLs",
@ -1667,5 +1667,294 @@
"段音频的主要内容": "The main content of the segment audio is",
"z$ 分别是空间直角坐标系中的三个坐标": "z$, respectively, are the three coordinates in the spatial rectangular coordinate system",
"这个是怎么识别的呢我也不清楚": "I'm not sure how this is recognized",
"从现在起": "From now on"
"从现在起": "From now on",
"连接bing搜索回答问题": "ConnectBingSearchAnswerQuestion",
"联网的ChatGPT_bing版": "OnlineChatGPT_BingEdition",
"Markdown翻译指定语言": "TranslateMarkdownToSpecifiedLanguage",
"Langchain知识库": "LangchainKnowledgeBase",
"Latex英文纠错加PDF对比": "CorrectEnglishInLatexWithPDFComparison",
"Latex输出PDF结果": "OutputPDFFromLatex",
"Latex翻译中文并重新编译PDF": "TranslateChineseToEnglishInLatexAndRecompilePDF",
"sprint亮靛": "SprintIndigo",
"寻找Latex主文件": "FindLatexMainFile",
"专业词汇声明": "ProfessionalTerminologyDeclaration",
"Latex精细分解与转化": "DecomposeAndConvertLatex",
"编译Latex": "CompileLatex",
"如果您是论文原作者": "If you are the original author of the paper",
"正在编译对比PDF": "Compiling the comparison PDF",
"将 \\include 命令转换为 \\input 命令": "Converting the \\include command to the \\input command",
"取评分最高者返回": "Returning the highest-rated one",
"不要修改!! 高危设置!通过修改此设置": "Do not modify!! High-risk setting! By modifying this setting",
"Tex源文件缺失": "Tex source file is missing!",
"6.25 加入判定latex模板的代码": "Added code to determine the latex template on June 25",
"正在精细切分latex文件": "Finely splitting the latex file",
"获取response失败": "Failed to get response",
"手动指定语言": "Manually specify the language",
"输入arxivID": "Enter arxivID",
"对输入的word文档进行摘要生成": "Generate a summary of the input word document",
"将指定目录下的PDF文件从英文翻译成中文": "Translate PDF files from English to Chinese in the specified directory",
"如果分析错误": "If the analysis is incorrect",
"尝试第": "Try the",
"用户填3": "User fills in 3",
"请在此处追加更细致的矫错指令": "Please append more detailed correction instructions here",
"为了防止大语言模型的意外谬误产生扩散影响": "To prevent the accidental spread of errors in large language models",
"前面是中文冒号": "The colon before is in Chinese",
"内含已经翻译的Tex文档": "Contains a Tex document that has been translated",
"成功啦": "Success!",
"刷新页面即可以退出UpdateKnowledgeArchive模式": "Refresh the page to exit UpdateKnowledgeArchive mode",
"或者不在环境变量PATH中": "Or not in the environment variable PATH",
"--读取文件": "--Read the file",
"才能继续下面的步骤": "To continue with the next steps",
"代理数据解析失败": "Proxy data parsing failed",
"详见项目主README.md": "See the main README.md of the project for details",
"临时存储用于调试": "Temporarily stored for debugging",
"屏蔽空行和太短的句子": "Filter out empty lines and sentences that are too short",
"gpt 多线程请求": "GPT multi-threaded request",
"编译已经开始": "Compilation has started",
"无法找到一个主Tex文件": "Cannot find a main Tex file",
"修复括号": "Fix parentheses",
"请您不要删除或修改这行警告": "Please do not delete or modify this warning",
"请登录OpenAI查看详情 https": "Please log in to OpenAI to view details at https",
"调用函数": "Call a function",
"请查看终端的输出或耐心等待": "Please check the output in the terminal or wait patiently",
"LatexEnglishCorrection+高亮修正位置": "Latex English correction + highlight correction position",
"行": "line",
"Newbing 请求失败": "Newbing request failed",
"转化PDF编译是否成功": "Check if the conversion to PDF and compilation were successful",
"建议更换代理协议": "Recommend changing the proxy protocol",
"========================================= 插件主程序1 =====================================================": "========================================= Plugin Main Program 1 =====================================================",
"终端": "terminal",
"请先上传文件素材": "Please upload file materials first",
"前面是中文逗号": "There is a Chinese comma in front",
"请尝试把以下指令复制到高级参数区": "Please try copying the following instructions to the advanced parameters section",
"翻译-": "Translation -",
"请耐心等待": "Please be patient",
"将前后断行符脱离": "Remove line breaks before and after",
"json等": "JSON, etc.",
"生成中文PDF": "Generate Chinese PDF",
"用红色标注处保留区": "Use red color to highlight the reserved area",
"对比PDF编译是否成功": "Compare if the PDF compilation was successful",
"回答完问题后": "After answering the question",
"其他操作系统表现未知": "Unknown performance on other operating systems",
"-构建知识库": "Build knowledge base",
"还原原文": "Restore original text",
"或者重启之后再度尝试": "Or try again after restarting",
"免费": "Free",
"仅在Windows系统进行了测试": "Tested only on Windows system",
"欢迎加REAME中的QQ联系开发者": "Feel free to contact the developer via QQ in REAME",
"当前知识库内的有效文件": "Valid files in the current knowledge base",
"您可以到Github Issue区": "You can go to the Github Issue area",
"刷新Gradio前端界面": "Refresh the Gradio frontend interface",
"吸收title与作者以上的部分": "Include the title and the above part of the author",
"给出一些判定模板文档的词作为扣分项": "Provide some words in the template document as deduction items",
"--读取参数": "-- Read parameters",
"然后进行问答": "And then perform question-answering",
"根据自然语言执行插件命令": "Execute plugin commands based on natural language",
"*{\\scriptsize\\textbf{警告": "*{\\scriptsize\\textbf{Warning",
"但请查收结果": "But please check the results",
"翻译内容可靠性无保障": "No guarantee of translation accuracy",
"寻找主文件": "Find the main file",
"消耗时间的函数": "Time-consuming function",
"当前语言模型温度设定": "Current language model temperature setting",
"这需要一段时间计算": "This requires some time to calculate",
"为啥chatgpt会把cite里面的逗号换成中文逗号呀": "Why does ChatGPT change commas inside 'cite' to Chinese commas?",
"发现已经存在翻译好的PDF文档": "Found an already translated PDF document",
"待提取的知识库名称id": "Knowledge base name ID to be extracted",
"文本碎片重组为完整的tex片段": "Reassemble text fragments into complete tex fragments",
"注意事项": "Notes",
"参数说明": "Parameter description",
"或代理节点": "Or proxy node",
"构建知识库": "Building knowledge base",
"报错信息如下. 如果是与网络相关的问题": "Error message as follows. If it is related to network issues",
"功能描述": "Function description",
"禁止移除或修改此警告": "Removal or modification of this warning is prohibited",
"Arixv翻译": "Arixv translation",
"读取优先级": "Read priority",
"包含documentclass关键字": "Contains the documentclass keyword",
"根据文本使用GPT模型生成相应的图像": "Generate corresponding images using GPT model based on the text",
"图像生成所用到的提示文本": "Prompt text used for image generation",
"Your account is not active. OpenAI以账户失效为由": "Your account is not active. OpenAI states that it is due to account expiration",
"快捷的调试函数": "Convenient debugging function",
"在多Tex文档中": "In multiple Tex documents",
"因此选择GenerateImage函数": "Therefore, choose the GenerateImage function",
"当前工作路径为": "The current working directory is",
"实际得到格式": "Obtained format in reality",
"这段代码定义了一个名为TempProxy的空上下文管理器": "This code defines an empty context manager named TempProxy",
"吸收其他杂项": "Absorb other miscellaneous items",
"请输入要翻译成哪种语言": "Please enter which language to translate into",
"的单词": "of the word",
"正在尝试自动安装": "Attempting automatic installation",
"如果有必要": "If necessary",
"开始下载": "Start downloading",
"项目Github地址 \\url{https": "Project GitHub address \\url{https",
"将根据报错信息修正tex源文件并重试": "The Tex source file will be corrected and retried based on the error message",
"发送至azure openai api": "Send to Azure OpenAI API",
"吸收匿名公式": "Absorb anonymous formulas",
"用该压缩包+ConversationHistoryArchive进行反馈": "Provide feedback using the compressed package + ConversationHistoryArchive",
"需要特殊依赖": "Requires special dependencies",
"还原部分原文": "Restore part of the original text",
"构建完成": "Build completed",
"解析arxiv网址失败": "Failed to parse arXiv URL",
"输入问题后点击该插件": "Click the plugin after entering the question",
"请求子进程": "Requesting subprocess",
"请务必用 pip install -r requirements.txt 指令安装依赖": "Please make sure to install the dependencies using the 'pip install -r requirements.txt' command",
"如果程序停顿5分钟以上": "If the program pauses for more than 5 minutes",
"转化PDF编译已经成功": "Conversion to PDF compilation was successful",
"虽然PDF生成失败了": "Although PDF generation failed",
"分析上述回答": "Analyze the above answer",
"吸收在42行以内的begin-end组合": "Absorb the begin-end combination within 42 lines",
"推荐http": "Recommend http",
"Latex没有安装": "Latex is not installed",
"用latex编译为PDF对修正处做高亮": "Compile to PDF using LaTeX and highlight the corrections",
"reverse 操作必须放在最后": "'reverse' operation must be placed at the end",
"AZURE OPENAI API拒绝了请求": "AZURE OPENAI API rejected the request",
"该项目的Latex主文件是": "The main LaTeX file of this project is",
"You are associated with a deactivated account. OpenAI以账户失效为由": "You are associated with a deactivated account. OpenAI considers it as an account expiration",
"它*必须*被包含在AVAIL_LLM_MODELS列表中": "It *must* be included in the AVAIL_LLM_MODELS list",
"未知指令": "Unknown command",
"尝试执行Latex指令失败": "Failed to execute the LaTeX command",
"摘要生成后的文档路径": "Path of the document after summary generation",
"GPT结果已输出": "GPT result has been outputted",
"使用Newbing": "Using Newbing",
"其他模型转化效果未知": "Unknown conversion effect of other models",
"P.S. 但愿没人把latex模板放在里面传进来": "P.S. Hopefully, no one passes a LaTeX template in it",
"定位主Latex文件": "Locate the main LaTeX file",
"后面是英文冒号": "English colon follows",
"文档越长耗时越长": "The longer the document, the longer it takes.",
"压缩包": "Compressed file",
"但通常不会出现在正文": "But usually does not appear in the body.",
"正在预热文本向量化模组": "Preheating text vectorization module",
"5刀": "5 dollars",
"提问吧! 但注意": "Ask questions! But be careful",
"发送至AZURE OPENAI API": "Send to AZURE OPENAI API",
"请仔细鉴别并以原文为准": "Please carefully verify and refer to the original text",
"如果需要使用AZURE 详情请见额外文档 docs\\use_azure.md": "If you need to use AZURE, please refer to the additional document docs\\use_azure.md for details",
"使用正则表达式查找半行注释": "Use regular expressions to find inline comments",
"只有第二步成功": "Only the second step is successful",
"P.S. 顺便把CTEX塞进去以支持中文": "P.S. By the way, include CTEX to support Chinese",
"安装方法https": "Installation method: https",
"则跳过GPT请求环节": "Then skip the GPT request process",
"请切换至“UpdateKnowledgeArchive”插件进行知识库访问": "Please switch to the 'UpdateKnowledgeArchive' plugin for knowledge base access",
"=================================== 工具函数 ===============================================": "=================================== Utility functions ===============================================",
"填入azure openai api的密钥": "Fill in the Azure OpenAI API key",
"上传Latex压缩包": "Upload LaTeX compressed file",
"远程云服务器部署": "Deploy to remote cloud server",
"用黑色标注转换区": "Use black color to annotate the conversion area",
"音频文件的路径": "Path to the audio file",
"必须包含documentclass": "Must include documentclass",
"再列出用户可能提出的三个问题": "List three more questions that the user might ask",
"根据需要切换prompt": "Switch the prompt as needed",
"将文件复制一份到下载区": "Make a copy of the file in the download area",
"次编译": "Second compilation",
"Latex文件融合完成": "LaTeX file merging completed",
"返回": "Return",
"后面是英文逗号": "Comma after this",
"对不同latex源文件扣分": "Deduct points for different LaTeX source files",
"失败啦": "Failed",
"编译BibTex": "Compile BibTeX",
"Linux下必须使用Docker安装": "Must install using Docker on Linux",
"报错信息": "Error message",
"删除或修改歧义文件": "Delete or modify ambiguous files",
"-预热文本向量化模组": "- Preheating text vectorization module",
"将每次对话记录写入Markdown格式的文件中": "Write each conversation record into a file in Markdown format",
"其他类型文献转化效果未知": "Unknown conversion effect for other types of literature",
"获取线程锁": "Acquire thread lock",
"使用英文": "Use English",
"如果存在调试缓存文件": "If there is a debug cache file",
"您需要首先调用构建知识库": "You need to call the knowledge base building first",
"原始PDF编译是否成功": "Whether the original PDF compilation is successful",
"生成 azure openai api请求": "Generate Azure OpenAI API requests",
"正在编译PDF": "Compiling PDF",
"仅调试": "Debug only",
"========================================= 插件主程序2 =====================================================": "========================================= Plugin Main Program 2 =====================================================",
"多线程翻译开始": "Multithreaded translation begins",
"出问题了": "There is a problem",
"版权归原文作者所有": "Copyright belongs to the original author",
"当前大语言模型": "Current large language model",
"目前对机器学习类文献转化效果最好": "Currently, the best conversion effect for machine learning literature",
"这个paper有个input命令文件名大小写错误": "This paper has an input command with a filename case error!",
"期望格式例如": "Expected format, for example",
"解决部分词汇翻译不准确的问题": "Resolve the issue of inaccurate translation for some terms",
"待注入的知识库名称id": "Name/ID of the knowledge base to be injected",
"精细切分latex文件": "Fine-grained segmentation of LaTeX files",
"永远给定None": "Always given None",
"work_folder = Latex预处理": "work_folder = LaTeX preprocessing",
"请直接去该路径下取回翻译结果": "Please directly go to the path to retrieve the translation results",
"寻找主tex文件": "Finding the main .tex file",
"模型参数": "Model parameters",
"返回找到的第一个": "Return the first one found",
"编译转化后的PDF": "Compile the converted PDF",
"\\SEAFILE_LOCALŅ03047\\我的资料库\\music\\Akie秋绘-未来轮廓.mp3": "\\SEAFILE_LOCALŅ03047\\My Library\\music\\Akie秋绘-未来轮廓.mp3",
"拆分过长的latex片段": "Splitting overly long LaTeX fragments",
"没有找到任何可读取文件": "No readable files found",
"暗色模式 / 亮色模式": "Dark mode / Light mode",
"检测到arxiv文档连接": "Detected arXiv document link",
"此插件Windows支持最佳": "This plugin has best support for Windows",
"from crazy_functions.虚空终端 import 终端": "from crazy_functions.null_terminal import Terminal",
"本地论文翻译": "Local paper translation",
"输出html调试文件": "Output HTML debugging file",
"以下所有配置也都支持利用环境变量覆写": "All the following configurations can also be overridden using environment variables",
"PDF文件所在的路径": "Path of the PDF file",
"也是可读的": "It is also readable",
"将消耗较长时间下载中文向量化模型": "Downloading Chinese vectorization model will take a long time",
"环境变量配置格式见docker-compose.yml": "See docker-compose.yml for the format of environment variable configuration",
"编译文献交叉引用": "Compile bibliographic cross-references",
"默认为default": "Default is 'default'",
"或者使用此插件继续上传更多文件": "Or use this plugin to continue uploading more files",
"该PDF由GPT-Academic开源项目调用大语言模型+Latex翻译插件一键生成": "This PDF is generated by the GPT-Academic open-source project using a large language model + LaTeX translation plugin",
"使用latexdiff生成论文转化前后对比": "Use latexdiff to generate before and after comparison of paper transformation",
"正在编译PDF文档": "Compiling PDF document",
"读取config.py文件中关于AZURE OPENAI API的信息": "Read the information about AZURE OPENAI API from the config.py file",
"配置教程&视频教程": "Configuration tutorial & video tutorial",
"临时地启动代理网络": "Temporarily start proxy network",
"临时地激活代理网络": "Temporarily activate proxy network",
"功能尚不稳定": "Functionality is unstable",
"默认为Chinese": "Default is Chinese",
"请查收结果": "Please check the results",
"将 chatglm 直接对齐到 chatglm2": "Align chatglm directly to chatglm2",
"中读取数据构建知识库": "Build a knowledge base by reading data in",
"用于给一小段代码上代理": "Used to proxy a small piece of code",
"分析结果": "Analysis results",
"依赖不足": "Insufficient dependencies",
"Markdown翻译": "Markdown translation",
"除非您是论文的原作者": "Unless you are the original author of the paper",
"test_LangchainKnowledgeBase读取": "test_LangchainKnowledgeBase read",
"将多文件tex工程融合为一个巨型tex": "Merge multiple tex projects into one giant tex",
"吸收iffalse注释": "Absorb iffalser comments",
"您接下来不能再使用其他插件了": "You can no longer use other plugins next",
"正在构建知识库": "Building knowledge base",
"需Latex": "Requires Latex",
"即找不到": "That is not found",
"保证括号正确": "Ensure parentheses are correct",
"= 2 通过一些Latex模板中常见": "= 2 through some common Latex templates",
"请立即终止程序": "Please terminate the program immediately",
"解压失败! 需要安装pip install rarfile来解压rar文件": "Decompression failed! Install 'pip install rarfile' to decompress rar files",
"请在此处给出自定义翻译命令": "Please provide custom translation command here",
"解压失败! 需要安装pip install py7zr来解压7z文件": "Decompression failed! Install 'pip install py7zr' to decompress 7z files",
"执行错误": "Execution error",
"目前仅支持GPT3.5/GPT4": "Currently only supports GPT3.5/GPT4",
"P.S. 顺便把Latex的注释去除": "P.S. Also remove comments from Latex",
"写出文件": "Write out the file",
"当前报错的latex代码处于第": "The current error in the LaTeX code is on line",
"主程序即将开始": "Main program is about to start",
"详情信息见requirements.txt": "See details in requirements.txt",
"释放线程锁": "Release thread lock",
"由于最为关键的转化PDF编译失败": "Due to the critical failure of PDF conversion and compilation",
"即将退出": "Exiting soon",
"尝试下载": "Attempting to download",
"删除整行的空注释": "Remove empty comments from the entire line",
"也找不到": "Not found either",
"从一批文件": "From a batch of files",
"编译结束": "Compilation finished",
"调用缓存": "Calling cache",
"只有GenerateImage和生成图像相关": "Only GenerateImage and image generation related",
"待处理的word文档路径": "Path of the word document to be processed",
"是否在提交时自动清空输入框": "Whether to automatically clear the input box upon submission",
"检查结果": "Check the result",
"生成时间戳": "Generate a timestamp",
"编译原始PDF": "Compile the original PDF",
"填入ENGINE": "Fill in ENGINE",
"填入api版本": "Fill in the API version",
"中文Bing版": "Chinese Bing version",
"当前支持的格式包括": "Currently supported formats include"
}

46
docs/use_audio.md Normal file
View File

@ -0,0 +1,46 @@
# 使用音频交互功能
## 1. 安装额外依赖
```
pip install --upgrade pyOpenSSL scipy git+https://github.com/aliyun/alibabacloud-nls-python-sdk.git
```
如果因为中国特色网络问题导致上述命令无法执行:
1. git clone alibabacloud-nls-python-sdk这个项目或者直接前往Github对应网址下载压缩包.
命令行输入: `git clone https://github.com/aliyun/alibabacloud-nls-python-sdk.git`
1. 进入alibabacloud-nls-python-sdk目录命令行输入`python setup.py install`
## 2. 配置音频功能开关 和 阿里云APPKEYconfig.py/config_private.py/环境变量)
```
ENABLE_AUDIO = True
ALIYUN_TOKEN = "554a50fcd0bb476c8d07bb630e94d20c" # 例如 f37f30e0f9934c34a992f6f64f7eba4f
ALIYUN_APPKEY = "RoPlZrM88DnAFkZK" # 例如 RoPlZrM88DnAFkZK
```
参考 https://help.aliyun.com/document_detail/450255.html
先有阿里云开发者账号,登录之后,需要开通 智能语音交互 的功能可以免费获得一个token然后在 全部项目 中创建一个项目可以获得一个appkey.
## 3.启动
启动gpt-academic `python main.py`
## 4.点击record from microphe授权音频采集
I 如果需要监听自己说话(不监听电脑音频),直接在浏览器中选择对应的麦即可
II 如果需要监听电脑音频(不监听自己说话),需要安装`VB-Audio VoiceMeeter`,打开声音控制面板(sound control panel)
- 1 `[把电脑的所有外放声音用VoiceMeeter截留]` 在输出区playback选项卡把VoiceMeeter Input虚拟设备set as default设为默认播放设备。
- 2 `[把截留的声音释放到gpt-academic]` 打开gpt-academic主界面授权音频采集后在浏览器地址栏或者类似的地方会出现一个麦克风图标打开后按照浏览器的提示选择VoiceMeeter虚拟麦克风。然后刷新页面重新授权音频采集。
- 3 `[把截留的声音同时释放到耳机或音响]` 完成第一步之后,您应处于听不到电脑声音的状态。为了在截获音频的同时,避免影响正常使用,请完成这最后一步配置。在声音控制面板(sound control panel)输入区recording选项卡把VoiceMeeter Output虚拟设备set as default。双击进入VoiceMeeter Output虚拟设备的设置。
- 3-1 进入VoiceMeeter Output虚拟设备子菜单打开listen选项卡。
- 3-2 勾选Listen to this device。
- 3-3 在playback through this device下拉菜单中选择你的正常耳机或音响。
III 两种音频监听模式切换时,需要刷新页面才有效。
## 5.点击函数插件区“实时音频采集” 或者其他音频交互功能

View File

@ -90,62 +90,29 @@
到现在为止,申请操作就完成了,需要记下来的有下面几个东西:
 密钥1或2都可以
● 密钥(对应AZURE_API_KEY1或2都可以
● 终结点
● 终结点 对应AZURE_ENDPOINT
 部署名对应AZURE_ENGINE不是模型名
● 部署名(不是模型名)
# 修改 config.py
```
AZURE_ENDPOINT = "填入终结点"
LLM_MODEL = "azure-gpt-3.5" # 指定启动时的默认模型当然事后从下拉菜单选也ok
AZURE_ENDPOINT = "填入终结点" # 见上述图片
AZURE_API_KEY = "填入azure openai api的密钥"
AZURE_API_VERSION = "2023-05-15" # 默认使用 2023-05-15 版本,无需修改
AZURE_ENGINE = "填入部署名"
```
# API的使用
接下来就是具体怎么使用API了还是可以参考官方文档[快速入门 - 开始通过 Azure OpenAI 服务使用 ChatGPT 和 GPT-4 - Azure OpenAI Service | Microsoft Learn](https://learn.microsoft.com/zh-cn/azure/cognitive-services/openai/chatgpt-quickstart?pivots=programming-language-python)
和openai自己的api调用有点类似都需要安装openai库不同的是调用方式
```
import openai
openai.api_type = "azure" #固定格式,无需修改
openai.api_base = os.getenv("AZURE_OPENAI_ENDPOINT") #这里填入“终结点”
openai.api_version = "2023-05-15" #固定格式,无需修改
openai.api_key = os.getenv("AZURE_OPENAI_KEY") #这里填入“密钥1”或“密钥2”
response = openai.ChatCompletion.create(
engine="gpt-35-turbo", #这里填入的不是模型名,是部署名
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Does Azure OpenAI support customer managed keys?"},
{"role": "assistant", "content": "Yes, customer managed keys are supported by Azure OpenAI."},
{"role": "user", "content": "Do other Azure Cognitive Services support this too?"}
]
)
print(response)
print(response['choices'][0]['message']['content'])
AZURE_ENGINE = "填入部署名" # 见上述图片
```
需要注意的是:
1.  engine那里填入的是部署名不是模型名
2.  通过openai库获得的这个 response 和通过 request 库访问 url 获得的 response 不同,不需要 decode已经是解析好的 json 了,直接根据键值读取即可。
更细节的使用方法详见官方API文档。
# 关于费用
Azure OpenAI API 还是需要一些费用的免费订阅只有1个月有效期,费用如下:
![image.png](https://note.youdao.com/yws/res/18095/WEBRESOURCEeba0ab6d3127b79e143ef2d5627c0e44)
Azure OpenAI API 还是需要一些费用的免费订阅只有1个月有效期
具体可以可以看这个网址 [Azure OpenAI 服务 - 定价| Microsoft Azure](https://azure.microsoft.com/zh-cn/pricing/details/cognitive-services/openai-service/?cdn=disable)

View File

@ -12,7 +12,7 @@ try {
live2d_settings['waifuTipsSize'] = '187x52';
live2d_settings['canSwitchModel'] = true;
live2d_settings['canSwitchTextures'] = true;
live2d_settings['canSwitchHitokoto'] = true;
live2d_settings['canSwitchHitokoto'] = false;
live2d_settings['canTakeScreenshot'] = false;
live2d_settings['canTurnToHomePage'] = false;
live2d_settings['canTurnToAboutPage'] = false;

View File

@ -34,10 +34,10 @@
"2": ["来自 Potion Maker 的 Tia 酱 ~"]
},
"hitokoto_api_message": {
"lwl12.com": ["这句一言来自 <span style=\"color:#ff99da;\">『{source}』</span>", ",是 <span style=\"color:#ff99da;\">{creator}</span> 投稿的", "。"],
"fghrsh.net": ["这句一言出处是 <span style=\"color:#ff99da;\">『{source}』</span>,是 <span style=\"color:#ff99da;\">FGHRSH</span> 在 {date} 收藏的!"],
"jinrishici.com": ["这句诗词出自 <span style=\"color:#ff99da;\">《{title}》</span>,是 {dynasty}诗人 {author} 创作的!"],
"hitokoto.cn": ["这句一言来自 <span style=\"color:#ff99da;\">『{source}』</span>,是 <span style=\"color:#ff99da;\">{creator}</span> 在 hitokoto.cn 投稿的。"]
"lwl12.com": ["这句一言来自 <span style=\"color:#0099cc;\">『{source}』</span>", ",是 <span style=\"color:#0099cc;\">{creator}</span> 投稿的", "。"],
"fghrsh.net": ["这句一言出处是 <span style=\"color:#0099cc;\">『{source}』</span>,是 <span style=\"color:#0099cc;\">FGHRSH</span> 在 {date} 收藏的!"],
"jinrishici.com": ["这句诗词出自 <span style=\"color:#0099cc;\">《{title}》</span>,是 {dynasty}诗人 {author} 创作的!"],
"hitokoto.cn": ["这句一言来自 <span style=\"color:#0099cc;\">『{source}』</span>,是 <span style=\"color:#0099cc;\">{creator}</span> 在 hitokoto.cn 投稿的。"]
}
},
"mouseover": [

View File

@ -1,778 +0,0 @@
#! .\venv\
# encoding: utf-8
# @Time : 2023/4/18
# @Author : Spike
# @Descr :
import ast
import copy
import hashlib
import io
import json
import os.path
import subprocess
import threading
import time
from concurrent.futures import ThreadPoolExecutor
import Levenshtein
import psutil
import re
import tempfile
import shutil
from contextlib import ExitStack
import logging
import yaml
import requests
import tiktoken
logger = logging
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
from scipy.linalg import norm
import pyperclip
import random
import gradio as gr
import toolbox
from prompt_generator import SqliteHandle
from bs4 import BeautifulSoup
import copy
"""contextlib 是 Python 标准库中的一个模块,提供了一些工具函数和装饰器,用于支持编写上下文管理器和处理上下文的常见任务,例如资源管理、异常处理等。
官网https://docs.python.org/3/library/contextlib.html"""
class Shell(object):
def __init__(self, args, stream=False):
self.args = args
self.subp = subprocess.Popen(args, shell=True,
stdin=subprocess.PIPE, stderr=subprocess.PIPE,
stdout=subprocess.PIPE, encoding='utf-8',
errors='ignore', close_fds=True)
self.__stream = stream
self.__temp = ''
def read(self):
logger.debug(f'The command being executed is: "{self.args}"')
if self.__stream:
sysout = self.subp.stdout
try:
with sysout as std:
for i in std:
logger.info(i.rstrip())
self.__temp += i
except KeyboardInterrupt as p:
return 3, self.__temp + self.subp.stderr.read()
finally:
return 3, self.__temp + self.subp.stderr.read()
else:
sysout = self.subp.stdout.read()
syserr = self.subp.stderr.read()
self.subp.stdin
if sysout:
logger.debug(f"{self.args} \n{sysout}")
return 1, sysout
elif syserr:
logger.error(f"{self.args} \n{syserr}")
return 0, syserr
else:
logger.debug(f"{self.args} \n{[sysout], [sysout]}")
return 2, '\n{}\n{}'.format(sysout, sysout)
def sync(self):
logger.debug('The command being executed is: "{}"'.format(self.args))
for i in self.subp.stdout:
logger.debug(i.rstrip())
self.__temp += i
yield self.__temp
for i in self.subp.stderr:
logger.debug(i.rstrip())
self.__temp += i
yield self.__temp
def timeStatistics(func):
"""
统计函数执行时常的装饰器
"""
def statistics(*args, **kwargs):
startTiem = time.time()
obj = func(*args, **kwargs)
endTiem = time.time()
ums = startTiem - endTiem
print('func:{} > Time-consuming: {}'.format(func, ums))
return obj
return statistics
def copy_temp_file(file):
if os.path.exists(file):
exdir = tempfile.mkdtemp()
temp_ = shutil.copy(file, os.path.join(exdir, os.path.basename(file)))
return temp_
else:
return None
def md5_str(st):
# 创建一个 MD5 对象
md5 = hashlib.md5()
# 更新 MD5 对象的内容
md5.update(str(st).encode())
# 获取加密后的结果
result = md5.hexdigest()
return result
def html_tag_color(tag, color=None, font='black'):
"""
将文本转换为带有高亮提示的html代码
"""
if not color:
rgb = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
color = f"rgb{rgb}"
tag = f'<span style="background-color: {color}; font-weight: bold; color: {font}">&nbsp;{tag}&ensp;</span>'
return tag
def html_a_blank(__href, name=''):
if not name:
name = __href
a = f'<a href="{__href}" target="_blank" class="svelte-xrr240">{name}</a>'
return a
def html_view_blank(__href, file_name=''):
if os.path.exists(__href):
__href = f'/file={__href}'
if not file_name:
file_name = __href.split('/')[-1]
a = f'<a href="{__href}" target="_blank" class="svelte-xrr240">{file_name}</a>'
return a
def html_iframe_code(html_file):
proxy, = toolbox.get_conf('LOCAL_PORT')
html_file = f'http://{ipaddr()}:{proxy}/file={html_file}'
ifr = f'<iframe width="100%" height="500px" frameborder="0" src="{html_file}"></iframe>'
return ifr
def html_download_blank(__href, file_name='temp', dir_name=''):
if os.path.exists(__href):
__href = f'/file={__href}'
if not dir_name:
dir_name = file_name
a = f'<a href="{__href}" target="_blank" download="{dir_name}" class="svelte-xrr240">{file_name}</a>'
return a
def html_local_img(__file):
a = f'<div align="center"><img src="file={__file}"></div>'
return a
def ipaddr():
# 获取本地ipx
ip = psutil.net_if_addrs()
for i in ip:
if ip[i][0][3]:
return ip[i][0][1]
def encryption_str(txt: str):
"""(关键字)(加密间隔)匹配机制(关键字间隔)"""
txt = str(txt)
pattern = re.compile(rf"(Authorization|WPS-Sid|Cookie)(:|\s+)\s*(\S+)[\s\S]*?(?=\n|$|\s)", re.IGNORECASE)
result = pattern.sub(lambda x: x.group(1) + ": XXXXXXXX", txt)
return result
def tree_out(dir=os.path.dirname(__file__), line=2, more=''):
"""
获取本地文件的树形结构转化为Markdown代码文本
"""
out = Shell(f'tree {dir} -F -I "__*|.*|venv|*.png|*.xlsx" -L {line} {more}').read()[1]
localfile = os.path.join(os.path.dirname(__file__), '.tree.md')
with open(localfile, 'w') as f:
f.write('```\n')
ll = out.splitlines()
for i in range(len(ll)):
if i == 0:
f.write(ll[i].split('/')[-2] + '\n')
else:
f.write(ll[i] + '\n')
f.write('```\n')
def chat_history(log: list, split=0):
"""
auto_gpt 使用的代码,后续会迁移
"""
if split:
log = log[split:]
chat = ''
history = ''
for i in log:
chat += f'{i[0]}\n\n'
history += f'{i[1]}\n\n'
return chat, history
def df_similarity(s1, s2):
"""弃用,会警告,这个库不会用"""
def add_space(s):
return ' '.join(list(s))
# 将字中间加入空格
s1, s2 = add_space(s1), add_space(s2)
# 转化为TF矩阵
cv = CountVectorizer(tokenizer=lambda s: s.split())
corpus = [s1, s2]
vectors = cv.fit_transform(corpus).toarray()
# 计算TF系数
return np.dot(vectors[0], vectors[1]) / (norm(vectors[0]) * norm(vectors[1]))
def check_json_format(file):
"""
检查上传的Json文件是否符合规范
"""
new_dict = {}
data = JsonHandle(file).load()
if type(data) is list and len(data) > 0:
if type(data[0]) is dict:
for i in data:
new_dict.update({i['act']: i['prompt']})
return new_dict
def json_convert_dict(file):
"""
批量将json转换为字典
"""
new_dict = {}
for root, dirs, files in os.walk(file):
for f in files:
if f.startswith('prompt') and f.endswith('json'):
new_dict.update(check_json_format(f))
return new_dict
def draw_results(txt, prompt: gr.Dataset, percent, switch, ipaddr: gr.Request):
"""
绘制搜索结果
Args:
txt (str): 过滤文本
prompt : 原始的dataset对象
percent (int): TF系数用于计算文本相似度
switch (list): 过滤个人或所有人的Prompt
ipaddr : 请求人信息
Returns:
注册函数所需的元祖对象
"""
data = diff_list(txt, percent=percent, switch=switch, hosts=ipaddr.client.host)
prompt.samples = data
return prompt.update(samples=data, visible=True), prompt
def diff_list(txt='', percent=0.70, switch: list = None, lst: dict = None, sp=15, hosts=''):
"""
按照搜索结果统计相似度的文本,两组文本相似度>70%的将统计在一起取最长的作为key
Args:
txt (str): 过滤文本
percent (int): TF系数用于计算文本相似度
switch (list): 过滤个人或所有人的Prompt
lst指定一个列表或字典
sp: 截取展示的文本长度
hosts : 请求人的ip
Returns:
返回一个列表
"""
count_dict = {}
is_all = toolbox.get_conf('prompt_list')[0]['key'][1]
if not lst:
lst = {}
tabs = SqliteHandle().get_tables()
if is_all in switch:
lst.update(SqliteHandle(f"ai_common_{hosts}").get_prompt_value(txt))
else:
for tab in tabs:
if tab.startswith('ai_common'):
lst.update(SqliteHandle(f"{tab}").get_prompt_value(txt))
lst.update(SqliteHandle(f"ai_private_{hosts}").get_prompt_value(txt))
# diff 数据根据precent系数归类数据
str_ = time.time()
def tf_factor_calcul(i):
found = False
dict_copy = count_dict.copy()
for key in dict_copy.keys():
str_tf = Levenshtein.jaro_winkler(i, key)
if str_tf >= percent:
if len(i) > len(key):
count_dict[i] = count_dict.copy()[key] + 1
count_dict.pop(key)
else:
count_dict[key] += 1
found = True
break
if not found: count_dict[i] = 1
with ThreadPoolExecutor(100) as executor:
executor.map(tf_factor_calcul, lst)
print('计算耗时', time.time()-str_)
sorted_dict = sorted(count_dict.items(), key=lambda x: x[1], reverse=True)
if switch:
sorted_dict += prompt_retrieval(is_all=switch, hosts=hosts, search=True)
dateset_list = []
for key in sorted_dict:
# 开始匹配关键字
index = str(key[0]).lower().find(txt.lower())
index_ = str(key[1]).lower().find(txt.lower())
if index != -1 or index_ != -1:
if index == -1: index = index_ # 增加搜索prompt 名称
# sp=split 用于判断在哪里启动、在哪里断开
if index - sp > 0:
start = index - sp
else:
start = 0
if len(key[0]) > sp * 2:
end = key[0][-sp:]
else:
end = ''
# 判断有没有传需要匹配的字符串,有则筛选、无则全返
if txt == '' and len(key[0]) >= sp:
show = key[0][0:sp] + " . . . " + end
show = show.replace('<', '')
elif txt == '' and len(key[0]) < sp:
show = key[0][0:sp]
show = show.replace('<', '')
else:
show = str(key[0][start:index + sp]).replace('<', '').replace(txt, html_tag_color(txt))
show += f" {html_tag_color(' X ' + str(key[1]))}"
if lst.get(key[0]):
be_value = lst[key[0]]
else:
be_value = None
value = be_value
dateset_list.append([show, key[0], value, key[1]])
return dateset_list
def prompt_upload_refresh(file, prompt, ipaddr: gr.Request):
"""
上传文件将文件转换为字典然后存储到数据库并刷新Prompt区域
Args:
file 上传的文件
prompt 原始prompt对象
ipaddripaddr用户请求信息
Returns:
注册函数所需的元祖对象
"""
hosts = ipaddr.client.host
if file.name.endswith('json'):
upload_data = check_json_format(file.name)
elif file.name.endswith('yaml'):
upload_data = YamlHandle(file.name).load()
else:
upload_data = {}
if upload_data != {}:
SqliteHandle(f'prompt_{hosts}').inset_prompt(upload_data)
ret_data = prompt_retrieval(is_all=['个人'], hosts=hosts)
return prompt.update(samples=ret_data, visible=True), prompt, ['个人']
else:
prompt.samples = [[f'{html_tag_color("数据解析失败,请检查文件是否符合规范", color="red")}', '']]
return prompt.samples, prompt, []
def prompt_retrieval(is_all, hosts='', search=False):
"""
上传文件将文件转换为字典然后存储到数据库并刷新Prompt区域
Args:
is_all prompt类型
hosts 查询的用户ip
search支持搜索搜索时将key作为key
Returns:
返回一个列表
"""
count_dict = {}
if '所有人' in is_all:
for tab in SqliteHandle('ai_common').get_tables():
if tab.startswith('prompt'):
data = SqliteHandle(tab).get_prompt_value(None)
if data: count_dict.update(data)
elif '个人' in is_all:
data = SqliteHandle(f'prompt_{hosts}').get_prompt_value(None)
if data: count_dict.update(data)
retrieval = []
if count_dict != {}:
for key in count_dict:
if not search:
retrieval.append([key, count_dict[key]])
else:
retrieval.append([count_dict[key], key])
return retrieval
else:
return retrieval
def prompt_reduce(is_all, prompt: gr.Dataset, ipaddr: gr.Request): # is_all, ipaddr: gr.Request
"""
上传文件将文件转换为字典然后存储到数据库并刷新Prompt区域
Args:
is_all prompt类型
prompt dataset原始对象
ipaddr请求用户信息
Returns:
返回注册函数所需的对象
"""
data = prompt_retrieval(is_all=is_all, hosts=ipaddr.client.host)
prompt.samples = data
return prompt.update(samples=data, visible=True), prompt, is_all
def prompt_save(txt, name, prompt: gr.Dataset, ipaddr: gr.Request):
"""
编辑和保存Prompt
Args:
txt Prompt正文
name Prompt的名字
prompt dataset原始对象
ipaddr请求用户信息
Returns:
返回注册函数所需的对象
"""
if txt and name:
yaml_obj = SqliteHandle(f'prompt_{ipaddr.client.host}')
yaml_obj.inset_prompt({name: txt})
result = prompt_retrieval(is_all=['个人'], hosts=ipaddr.client.host)
prompt.samples = result
return "", "", ['个人'], prompt.update(samples=result, visible=True), prompt, gr.Tabs.update(selected='chatbot')
elif not txt or not name:
result = [[f'{html_tag_color("编辑框 or 名称不能为空!!!!!", color="red")}', '']]
prompt.samples = [[f'{html_tag_color("编辑框 or 名称不能为空!!!!!", color="red")}', '']]
return txt, name, [], prompt.update(samples=result, visible=True), prompt, gr.Tabs.update(selected='chatbot')
def prompt_input(txt: str, prompt_str, name_str, index, data: gr.Dataset, tabs_index):
"""
点击dataset的值使用Prompt
Args:
txt 输入框正文
index 点击的Dataset下标
data dataset原始对象
Returns:
返回注册函数所需的对象
"""
data_str = str(data.samples[index][1])
data_name = str(data.samples[index][0])
rp_str = '{{{v}}}'
def str_v_handle(__str):
if data_str.find(rp_str) != -1 and __str:
txt_temp = data_str.replace(rp_str, __str)
elif __str:
txt_temp = data_str + '\n' + __str
else:
txt_temp = data_str
return txt_temp
if tabs_index == 1:
new_txt = str_v_handle(prompt_str)
return txt, new_txt, data_name
else:
new_txt = str_v_handle(txt)
return new_txt, prompt_str, name_str
def copy_result(history):
"""复制history"""
if history != []:
pyperclip.copy(history[-1])
return '已将结果复制到剪切板'
else:
return "无对话记录,复制错误!!"
def str_is_list(s):
try:
list_ast = ast.literal_eval(s)
return isinstance(list_ast, list)
except (SyntaxError, ValueError):
return False
def show_prompt_result(index, data: gr.Dataset, chatbot, pro_edit, pro_name):
"""
查看Prompt的对话记录结果
Args:
index 点击的Dataset下标
data dataset原始对象
chatbot聊天机器人
Returns:
返回注册函数所需的对象
"""
click = data.samples[index]
if str_is_list(click[2]):
list_copy = eval(click[2])
for i in range(0, len(list_copy), 2):
if i + 1 >= len(list_copy): # 如果下标越界了,单独处理最后一个元素
chatbot.append([list_copy[i]])
else:
chatbot.append([list_copy[i], list_copy[i + 1]])
elif click[2] is None and pro_edit == '':
pro_edit = click[1]
pro_name = click[3]
else:
chatbot.append((click[1], click[2]))
return chatbot, pro_edit, pro_name
def pattern_html(html):
bs = BeautifulSoup(str(html), 'html.parser')
md_message = bs.find('div', {'class': 'md-message'})
if md_message:
return md_message.get_text(separator='')
else:
return ""
def thread_write_chat(chatbot, history):
"""
对话记录写入数据库
"""
chatbot, history = copy.copy(chatbot), copy.copy(history)
private_key = toolbox.get_conf('private_key')[0]
chat_title = chatbot[0][1].split()
i_say = pattern_html(chatbot[-1][0])
if history:
gpt_result = history
else: # 如果历史对话不存在,那么读取对话框
gpt_result = [pattern_html(v) for i in chatbot for v in i]
if private_key in chat_title:
SqliteHandle(f'ai_private_{chat_title[-2]}').inset_prompt({i_say: gpt_result})
else:
SqliteHandle(f'ai_common_{chat_title[-2]}').inset_prompt({i_say: gpt_result})
base_path = os.path.dirname(__file__)
prompt_path = os.path.join(base_path, 'users_data')
users_path = os.path.join(base_path, 'private_upload')
logs_path = os.path.join(base_path, 'gpt_log')
def reuse_chat(result, chatbot, history, pro_numb, say):
"""复用对话记录"""
if result is None or result == []:
return chatbot, history, gr.update(), gr.update(), '', gr.Column.update()
else:
if pro_numb:
chatbot += result
history += [pattern_html(_) for i in result for _ in i]
else:
chatbot.append(result[-1])
history += [pattern_html(_) for i in result[-2:] for _ in i]
print(chatbot[-1][0])
return chatbot, history, say, gr.Tabs.update(selected='chatbot'), '', gr.Column.update(visible=False)
def num_tokens_from_string(listing: list, encoding_name: str = 'cl100k_base') -> int:
"""Returns the number of tokens in a text string."""
count_tokens = 0
for i in listing:
encoding = tiktoken.get_encoding(encoding_name)
count_tokens += len(encoding.encode(i))
return count_tokens
def spinner_chatbot_loading(chatbot):
loading = [''.join(['.' * random.randint(1, 5)])]
# 将元组转换为列表并修改元素
loading_msg = copy.deepcopy(chatbot)
temp_list = list(loading_msg[-1])
temp_list[1] = pattern_html(temp_list[1]) + f'{random.choice(loading)}'
# 将列表转换回元组并替换原始元组
loading_msg[-1] = tuple(temp_list)
return loading_msg
def refresh_load_data(chat, history, prompt, crazy_list, request: gr.Request):
"""
Args:
chat: 聊天组件
history: 对话记录
prompt: prompt dataset组件
Returns:
预期是每次刷新页面,加载最新
"""
is_all = toolbox.get_conf('prompt_list')[0]['key'][0]
data = prompt_retrieval(is_all=[is_all])
prompt.samples = data
selected = random.sample(crazy_list, 4)
user_agent = request.kwargs['headers']['user-agent'].lower()
if user_agent.find('android') != -1 or user_agent.find('iphone') != -1:
hied_elem = gr.update(visible=False)
else:
hied_elem = gr.update()
outputs = [prompt.update(samples=data, visible=True), prompt,
chat, history, gr.Dataset.update(samples=[[i] for i in selected]), selected,
hied_elem, hied_elem]
return outputs
def txt_converter_json(input_string):
try:
if input_string.startswith("{") and input_string.endswith("}"):
# 尝试将字符串形式的字典转换为字典对象
dict_object = ast.literal_eval(input_string)
else:
# 尝试将字符串解析为JSON对象
dict_object = json.loads(input_string)
formatted_json_string = json.dumps(dict_object, indent=4, ensure_ascii=False)
return formatted_json_string
except (ValueError, SyntaxError):
return input_string
def clean_br_string(s):
s = re.sub('<\s*br\s*/?>', '\n', s) # 使用正则表达式同时匹配<br>、<br/>、<br />、< br>和< br/>
return s
def update_btn(self,
value: str = None,
variant: str = None,
visible: bool = None,
interactive: bool = None,
elem_id: str = None,
label: str = None
):
if not variant: variant = self.variant
if not visible: visible = self.visible
if not value: value = self.value
if not interactive: interactive = self.interactive
if not elem_id: elem_id = self.elem_id
if not elem_id: label = self.label
return {
"variant": variant,
"visible": visible,
"value": value,
"interactive": interactive,
'elem_id': elem_id,
'label': label,
"__type__": "update",
}
def update_txt(self,
value: str = None,
lines: int = None,
max_lines: int = None,
placeholder: str = None,
label: str = None,
show_label: bool = None,
visible: bool = None,
interactive: bool = None,
type: str = None,
elem_id: str = None
):
return {
"lines": self.lines,
"max_lines": self.max_lines,
"placeholder": self.placeholder,
"label": self.label,
"show_label": self.show_label,
"visible": self.visible,
"value": self.value,
"type": self.type,
"interactive": self.interactive,
"elem_id": elem_id,
"__type__": "update",
}
def get_html(filename):
path = os.path.join(base_path, "docs/assets", "html", filename)
if os.path.exists(path):
with open(path, encoding="utf8") as file:
return file.read()
return ""
def git_log_list():
ll = Shell("git log --pretty=format:'%s | %h' -n 10").read()[1].splitlines()
return [i.split('|') for i in ll if 'branch' not in i][:5]
import qrcode
from PIL import Image, ImageDraw
def qr_code_generation(data, icon_path=None, file_name='qc_icon.png'):
# 创建qrcode对象
qr = qrcode.QRCode(version=2, error_correction=qrcode.constants.ERROR_CORRECT_Q, box_size=10, border=2,)
qr.add_data(data)
# 创建二维码图片
img = qr.make_image()
# 图片转换为RGBA格式
img = img.convert('RGBA')
# 返回二维码图片的大小
img_w, img_h = img.size
# 打开logo
if not icon_path:
icon_path = os.path.join(base_path, 'docs/assets/PLAI.jpeg')
logo = Image.open(icon_path)
# logo大小为二维码的四分之一
logo_w = img_w // 4
logo_h = img_w // 4
# 修改logo图片大小
logo = logo.resize((logo_w, logo_h), Image.LANCZOS) # or Image.Resampling.LANCZOS
# 把logo放置在二维码中间
w = (img_w - logo_w) // 2
h = (img_h - logo_h) // 2
img.paste(logo, (w, h))
qr_path = os.path.join(logs_path, 'file_name')
img.save()
return qr_path
class YamlHandle:
def __init__(self, file=os.path.join(prompt_path, 'ai_common.yaml')):
if not os.path.exists(file):
Shell(f'touch {file}').read()
self.file = file
self._load = self.load()
def load(self) -> dict:
with open(file=self.file, mode='r') as f:
data = yaml.safe_load(f)
return data
def update(self, key, value):
date = self._load
if not date:
date = {}
date[key] = value
with open(file=self.file, mode='w') as f:
yaml.dump(date, f, allow_unicode=True)
return date
def dump_dict(self, new_dict):
date = self._load
if not date:
date = {}
date.update(new_dict)
with open(file=self.file, mode='w') as f:
yaml.dump(date, f, allow_unicode=True)
return date
class JsonHandle:
def __init__(self, file):
self.file = file
def load(self) -> object:
with open(self.file, 'r') as f:
data = json.load(f)
return data
if __name__ == '__main__':
pass

76
main.py
View File

@ -4,22 +4,23 @@ def main():
import gradio as gr
if gr.__version__ not in ['3.28.3','3.32.2']: assert False, "需要特殊依赖,请务必用 pip install -r requirements.txt 指令安装依赖详情信息见requirements.txt"
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
from toolbox import format_io, find_free_port, on_file_uploaded, on_report_generated, get_conf, ArgsGeneralWrapper, load_chat_cookies, DummyWith
# 建议您复制一个config_private.py放自己的秘密, 如API和代理网址, 避免不小心传github被别人看到
proxies, WEB_PORT, LLM_MODEL, CONCURRENT_COUNT, AUTHENTICATION, CHATBOT_HEIGHT, LAYOUT, API_KEY, AVAIL_LLM_MODELS, AUTO_CLEAR_TXT = \
get_conf('proxies', 'WEB_PORT', 'LLM_MODEL', 'CONCURRENT_COUNT', 'AUTHENTICATION', 'CHATBOT_HEIGHT', 'LAYOUT', 'API_KEY', 'AVAIL_LLM_MODELS', 'AUTO_CLEAR_TXT')
proxies, WEB_PORT, LLM_MODEL, CONCURRENT_COUNT, AUTHENTICATION, CHATBOT_HEIGHT, LAYOUT, AVAIL_LLM_MODELS, AUTO_CLEAR_TXT = \
get_conf('proxies', 'WEB_PORT', 'LLM_MODEL', 'CONCURRENT_COUNT', 'AUTHENTICATION', 'CHATBOT_HEIGHT', 'LAYOUT', 'AVAIL_LLM_MODELS', 'AUTO_CLEAR_TXT')
ENABLE_AUDIO, AUTO_CLEAR_TXT = get_conf('ENABLE_AUDIO', 'AUTO_CLEAR_TXT')
# 如果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
from theme.theme import adjust_theme, advanced_css, theme_declaration
initial_prompt = "Serve me as a writing and programming assistant."
title_html = f"<h1 align=\"center\">ChatGPT 学术优化 {get_current_version()}</h1>"
title_html = f"<h1 align=\"center\">GPT 学术优化 {get_current_version()}</h1>{theme_declaration}"
description = """代码开源和更新[地址🚀](https://github.com/binary-husky/chatgpt_academic),感谢热情的[开发者们❤️](https://github.com/binary-husky/chatgpt_academic/graphs/contributors)"""
# 问询记录, python 版本建议3.9+(越新越好)
import logging
import logging, uuid
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)
@ -37,7 +38,6 @@ def main():
gr.Chatbot.postprocess = format_io
# 做一些外观色彩上的调整
from theme import adjust_theme, advanced_css
set_theme = adjust_theme()
# 代理与自动更新
@ -45,23 +45,23 @@ def main():
proxy_info = check_proxy(proxies)
gr_L1 = lambda: gr.Row().style()
gr_L2 = lambda scale: gr.Column(scale=scale)
gr_L2 = lambda scale, elem_id: gr.Column(scale=scale, elem_id=elem_id)
if LAYOUT == "TOP-DOWN":
gr_L1 = lambda: DummyWith()
gr_L2 = lambda scale: gr.Row()
gr_L2 = lambda scale, elem_id: gr.Row()
CHATBOT_HEIGHT /= 2
cancel_handles = []
with gr.Blocks(title="ChatGPT 学术优化", theme=set_theme, analytics_enabled=False, css=advanced_css) as demo:
with gr.Blocks(title="GPT 学术优化", 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})
cookies = gr.State(load_chat_cookies())
with gr_L1():
with gr_L2(scale=2):
chatbot = gr.Chatbot(label=f"当前模型:{LLM_MODEL}")
chatbot.style(height=CHATBOT_HEIGHT)
with gr_L2(scale=2, elem_id="gpt-chat"):
chatbot = gr.Chatbot(label=f"当前模型:{LLM_MODEL}", elem_id="gpt-chatbot")
if LAYOUT == "TOP-DOWN": chatbot.style(height=CHATBOT_HEIGHT)
history = gr.State([])
with gr_L2(scale=1):
with gr.Accordion("输入区", open=True) as area_input_primary:
with gr_L2(scale=1, elem_id="gpt-panel"):
with gr.Accordion("输入区", open=True, elem_id="input-panel") as area_input_primary:
with gr.Row():
txt = gr.Textbox(show_label=False, placeholder="Input question here.").style(container=False)
with gr.Row():
@ -70,17 +70,20 @@ def main():
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")
if ENABLE_AUDIO:
with gr.Row():
audio_mic = gr.Audio(source="microphone", type="numpy", streaming=True, show_label=False).style(container=False)
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:
status = gr.Markdown(f"Tip: 按Enter提交, 按Shift+Enter换行。当前模型: {LLM_MODEL} \n {proxy_info}", elem_id="state-panel")
with gr.Accordion("基础功能区", open=True, elem_id="basic-panel") as area_basic_fn:
with gr.Row():
for k in functional:
if ("Visible" in functional[k]) and (not functional[k]["Visible"]): continue
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.Accordion("函数插件区", open=True, elem_id="plugin-panel") as area_crazy_fn:
with gr.Row():
gr.Markdown("注意:以下“红颜色”标识的函数插件需从输入区读取路径作为参数.")
gr.Markdown("插件可读取“输入区”文本/路径作为参数(上传文件自动修正路径)")
with gr.Row():
for k in crazy_fns:
if not crazy_fns[k].get("AsButton", True): continue
@ -91,16 +94,16 @@ def main():
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.Row():
dropdown = gr.Dropdown(dropdown_fn_list, value=r"打开插件列表", label="").style(container=False)
dropdown = gr.Dropdown(dropdown_fn_list, value=r"打开插件列表", label="", show_label=False).style(container=False)
with gr.Row():
plugin_advanced_arg = gr.Textbox(show_label=True, label="高级参数输入区", visible=False,
placeholder="这里是特殊函数插件的高级参数输入区").style(container=False)
with gr.Row():
switchy_bt = gr.Button(r"请先从插件列表中选择", variant="secondary")
with gr.Row():
with gr.Accordion("点击展开“文件上传区”。上传本地文件可供红色函数插件调用。", open=False) as area_file_up:
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")):
with gr.Accordion("更换模型 & SysPrompt & 交互界面布局", open=(LAYOUT == "TOP-DOWN"), elem_id="interact-panel"):
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",)
@ -109,7 +112,7 @@ def main():
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.Accordion("备选输入区", open=True, visible=False, elem_id="input-panel2") 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():
@ -130,9 +133,9 @@ def main():
ret.update({plugin_advanced_arg: 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, clearBtn, clearBtn2, plugin_advanced_arg] )
checkboxes.select(fn_area_visibility, [checkboxes], [area_basic_fn, area_crazy_fn, area_input_primary, area_input_secondary, txt, txt2, clearBtn, clearBtn2, plugin_advanced_arg] )
# 整理反复出现的控件句柄组合
input_combo = [cookies, max_length_sl, md_dropdown, txt, top_p, temperature, chatbot, history, system_prompt, plugin_advanced_arg]
input_combo = [cookies, max_length_sl, md_dropdown, txt, txt2, top_p, temperature, chatbot, history, system_prompt, plugin_advanced_arg]
output_combo = [cookies, chatbot, history, status]
predict_args = dict(fn=ArgsGeneralWrapper(predict), inputs=input_combo, outputs=output_combo)
# 提交按钮、重置按钮
@ -155,7 +158,7 @@ def main():
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])
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
@ -174,18 +177,31 @@ def main():
dropdown.select(on_dropdown_changed, [dropdown], [switchy_bt, plugin_advanced_arg] )
def on_md_dropdown_changed(k):
return {chatbot: gr.update(label="当前模型:"+k)}
md_dropdown.select(on_md_dropdown_changed, [md_dropdown], [chatbot])
md_dropdown.select(on_md_dropdown_changed, [md_dropdown], [chatbot] )
# 随变按钮的回调函数注册
def route(k, *args, **kwargs):
def route(request: gr.Request, k, *args, **kwargs):
if k in [r"打开插件列表", r"请先从插件列表中选择"]: return
yield from ArgsGeneralWrapper(crazy_fns[k]["Function"])(*args, **kwargs)
yield from ArgsGeneralWrapper(crazy_fns[k]["Function"])(request, *args, **kwargs)
click_handle = switchy_bt.click(route,[switchy_bt, *input_combo, gr.State(PORT)], output_combo)
click_handle.then(on_report_generated, [cookies, file_upload, chatbot], [cookies, file_upload, chatbot])
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)
if ENABLE_AUDIO:
from crazy_functions.live_audio.audio_io import RealtimeAudioDistribution
rad = RealtimeAudioDistribution()
def deal_audio(audio, cookies):
rad.feed(cookies['uuid'].hex, audio)
audio_mic.stream(deal_audio, inputs=[audio_mic, cookies])
def init_cookie(cookies, chatbot):
# 为每一位访问的用户赋予一个独一无二的uuid编码
cookies.update({'uuid': uuid.uuid4()})
return cookies
demo.load(init_cookie, inputs=[cookies, chatbot], outputs=[cookies])
demo.load(lambda: 0, inputs=None, outputs=None, _js='()=>{ChatBotHeight();}')
# gradio的inbrowser触发不太稳定回滚代码到原始的浏览器打开函数
def auto_opentab_delay():
import threading, webbrowser, time

View File

@ -33,7 +33,7 @@ import pickle
import time
CACHE_FOLDER = "gpt_log"
blacklist = ['multi-language', 'gpt_log', '.git', 'private_upload', 'multi_language.py']
blacklist = ['multi-language', 'gpt_log', '.git', 'private_upload', 'multi_language.py', 'build', '.github', '.vscode', '__pycache__', 'venv']
# LANG = "TraditionalChinese"
# TransPrompt = f"Replace each json value `#` with translated results in Traditional Chinese, e.g., \"原始文本\":\"翻譯後文字\". Keep Json format. Do not answer #."
@ -301,6 +301,7 @@ def step_1_core_key_translate():
elif isinstance(node, ast.ImportFrom):
for n in node.names:
if contains_chinese(n.name): syntax.append(n.name)
# if node.module is None: print(node.module)
for k in node.module.split('.'):
if contains_chinese(k): syntax.append(k)
return syntax
@ -310,6 +311,7 @@ def step_1_core_key_translate():
for root, dirs, files in os.walk(directory_path):
if any([b in root for b in blacklist]):
continue
print(files)
for file in files:
if file.endswith('.py'):
file_path = os.path.join(root, file)
@ -505,6 +507,6 @@ def step_2_core_key_translate():
with open(file_path_new, 'w', encoding='utf-8') as f:
f.write(content)
os.remove(file_path)
step_1_core_key_translate()
step_2_core_key_translate()
print('Finished, checkout generated results at ./multi-language/')

View File

@ -1,102 +0,0 @@
#! .\venv\
# encoding: utf-8
# @Time : 2023/4/19
# @Author : Spike
# @Descr :
import os.path
import sqlite3
import threading
import functools
import func_box
# 连接到数据库
base_path = os.path.dirname(__file__)
prompt_path = os.path.join(base_path, 'users_data')
def connect_db_close(cls_method):
@functools.wraps(cls_method)
def wrapper(cls=None, *args, **kwargs):
cls._connect_db()
result = cls_method(cls, *args, **kwargs)
cls._close_db()
return result
return wrapper
class SqliteHandle:
def __init__(self, table='ai_common', database='ai_prompt.db'):
self.__database = database
self.__connect = sqlite3.connect(os.path.join(prompt_path, self.__database))
self.__cursor = self.__connect.cursor()
self.__table = table
if self.__table not in self.get_tables():
self.create_tab()
def new_connect_db(self):
"""多线程操作时每个线程新建独立的connect"""
self.__connect = sqlite3.connect(os.path.join(prompt_path, self.__database))
self.__cursor = self.__connect.cursor()
def new_close_db(self):
self.__cursor.close()
self.__connect.close()
def create_tab(self):
self.__cursor.execute(f"CREATE TABLE `{self.__table}` ('prompt' TEXT UNIQUE, 'result' TEXT)")
def get_tables(self):
all_tab = []
result = self.__cursor.execute("SELECT name FROM sqlite_master WHERE type = 'table';")
for tab in result:
all_tab.append(tab[0])
return all_tab
def get_prompt_value(self, find=None):
temp_all = {}
if find:
result = self.__cursor.execute(f"SELECT prompt, result FROM `{self.__table}` WHERE prompt LIKE '%{find}%'").fetchall()
else:
result = self.__cursor.execute(f"SELECT prompt, result FROM `{self.__table}`").fetchall()
for row in result:
temp_all[row[0]] = row[1]
return temp_all
def inset_prompt(self, prompt: dict):
for key in prompt:
self.__cursor.execute(f"REPLACE INTO `{self.__table}` (prompt, result) VALUES (?, ?);", (str(key), str(prompt[key])))
self.__connect.commit()
def delete_prompt(self, name):
self.__cursor.execute(f"DELETE from `{self.__table}` where prompt LIKE '{name}'")
self.__connect.commit()
def delete_tabls(self, tab):
self.__cursor.execute(f"DROP TABLE `{tab}`;")
self.__connect.commit()
def find_prompt_result(self, name):
query = self.__cursor.execute(f"SELECT result FROM `{self.__table}` WHERE prompt LIKE '{name}'").fetchall()
if query == []:
query = self.__cursor.execute(f"SELECT result FROM `prompt_127.0.0.1` WHERE prompt LIKE '{name}'").fetchall()
return query[0][0]
else:
return query[0][0]
def cp_db_data(incloud_tab='prompt'):
sql_ll = sqlite_handle(database='ai_prompt_cp.db')
tabs = sql_ll.get_tables()
for i in tabs:
if str(i).startswith(incloud_tab):
old_data = sqlite_handle(table=i, database='ai_prompt_cp.db').get_prompt_value()
sqlite_handle(table=i).inset_prompt(old_data)
def inset_127_prompt():
sql_handle = sqlite_handle(table='prompt_127.0.0.1')
prompt_json = os.path.join(prompt_path, 'prompts-PlexPt.json')
data_list = func_box.JsonHandle(prompt_json).load()
for i in data_list:
sql_handle.inset_prompt(prompt={i['act']: i['prompt']})
sqlite_handle = SqliteHandle
if __name__ == '__main__':
cp_db_data()

View File

@ -13,21 +13,12 @@ from functools import lru_cache
from concurrent.futures import ThreadPoolExecutor
from toolbox import get_conf, trimmed_format_exc
from request_llm.bridge_chatgpt import predict_no_ui_long_connection as chatgpt_noui
from request_llm.bridge_chatgpt import predict as chatgpt_ui
from .bridge_azure_test import predict_no_ui_long_connection as azure_noui
from .bridge_azure_test import predict as azure_ui
from .bridge_azure_test import predict_no_ui_long_connection as azure_noui
from .bridge_azure_test import predict as azure_ui
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_newbing import predict_no_ui_long_connection as newbing_noui
from .bridge_newbing import predict as newbing_ui
# from .bridge_tgui import predict_no_ui_long_connection as tgui_noui
# from .bridge_tgui import predict as tgui_ui
@ -54,11 +45,11 @@ class LazyloadTiktoken(object):
return encoder.decode(*args, **kwargs)
# Endpoint 重定向
API_URL_REDIRECT, PROXY_API_URL = get_conf("API_URL_REDIRECT", 'PROXY_API_URL')
API_URL_REDIRECT, AZURE_ENDPOINT, AZURE_ENGINE = get_conf("API_URL_REDIRECT", "AZURE_ENDPOINT", "AZURE_ENGINE")
openai_endpoint = "https://api.openai.com/v1/chat/completions"
api2d_endpoint = "https://openai.api2d.net/v1/chat/completions"
newbing_endpoint = "wss://sydney.bing.com/sydney/ChatHub"
proxy_endpoint = PROXY_API_URL
azure_endpoint = AZURE_ENDPOINT + f'openai/deployments/{AZURE_ENGINE}/chat/completions?api-version=2023-05-15'
# 兼容旧版的配置
try:
API_URL, = get_conf("API_URL")
@ -73,7 +64,6 @@ if api2d_endpoint in API_URL_REDIRECT: api2d_endpoint = API_URL_REDIRECT[api2d_e
if newbing_endpoint in API_URL_REDIRECT: newbing_endpoint = API_URL_REDIRECT[newbing_endpoint]
# 获取tokenizer
tokenizer_gpt35 = LazyloadTiktoken("gpt-3.5-turbo")
tokenizer_gpt4 = LazyloadTiktoken("gpt-4")
@ -127,21 +117,12 @@ model_info = {
"tokenizer": tokenizer_gpt4,
"token_cnt": get_token_num_gpt4,
},
# azure openai
"azure-gpt35":{
"fn_with_ui": azure_ui,
"fn_without_ui": azure_noui,
"endpoint": get_conf("AZURE_ENDPOINT"),
"max_token": 4096,
"tokenizer": tokenizer_gpt35,
"token_cnt": get_token_num_gpt35,
},
# azure openai
"azure-gpt35":{
"fn_with_ui": azure_ui,
"fn_without_ui": azure_noui,
"endpoint": get_conf("AZURE_ENDPOINT"),
"azure-gpt-3.5":{
"fn_with_ui": chatgpt_ui,
"fn_without_ui": chatgpt_noui,
"endpoint": azure_endpoint,
"max_token": 4096,
"tokenizer": tokenizer_gpt35,
"token_cnt": get_token_num_gpt35,
@ -161,9 +142,9 @@ model_info = {
"fn_with_ui": chatgpt_ui,
"fn_without_ui": chatgpt_noui,
"endpoint": api2d_endpoint,
"max_token": 4096,
"tokenizer": tokenizer_gpt35,
"token_cnt": get_token_num_gpt35,
"max_token": 8192,
"tokenizer": tokenizer_gpt4,
"token_cnt": get_token_num_gpt4,
},
# 将 chatglm 直接对齐到 chatglm2
@ -183,21 +164,12 @@ model_info = {
"tokenizer": tokenizer_gpt35,
"token_cnt": get_token_num_gpt35,
},
# newbing
"newbing": {
"fn_with_ui": newbing_ui,
"fn_without_ui": newbing_noui,
"endpoint": newbing_endpoint,
"max_token": 4096,
"tokenizer": tokenizer_gpt35,
"token_cnt": get_token_num_gpt35,
},
}
AVAIL_LLM_MODELS, = get_conf("AVAIL_LLM_MODELS")
AVAIL_LLM_MODELS, LLM_MODEL = get_conf("AVAIL_LLM_MODELS", "LLM_MODEL")
AVAIL_LLM_MODELS = AVAIL_LLM_MODELS + [LLM_MODEL]
if "jittorllms_rwkv" in AVAIL_LLM_MODELS:
from .bridge_jittorllms_rwkv import predict_no_ui_long_connection as rwkv_noui
from .bridge_jittorllms_rwkv import predict as rwkv_ui
@ -281,6 +253,41 @@ if "newbing-free" in AVAIL_LLM_MODELS:
})
except:
print(trimmed_format_exc())
if "newbing" in AVAIL_LLM_MODELS: # same with newbing-free
try:
from .bridge_newbingfree import predict_no_ui_long_connection as newbingfree_noui
from .bridge_newbingfree import predict as newbingfree_ui
# claude
model_info.update({
"newbing": {
"fn_with_ui": newbingfree_ui,
"fn_without_ui": newbingfree_noui,
"endpoint": newbing_endpoint,
"max_token": 4096,
"tokenizer": tokenizer_gpt35,
"token_cnt": get_token_num_gpt35,
}
})
except:
print(trimmed_format_exc())
if "chatglmft" in AVAIL_LLM_MODELS: # same with newbing-free
try:
from .bridge_chatglmft import predict_no_ui_long_connection as chatglmft_noui
from .bridge_chatglmft import predict as chatglmft_ui
# claude
model_info.update({
"chatglmft": {
"fn_with_ui": chatglmft_ui,
"fn_without_ui": chatglmft_noui,
"endpoint": None,
"max_token": 4096,
"tokenizer": tokenizer_gpt35,
"token_cnt": get_token_num_gpt35,
}
})
except:
print(trimmed_format_exc())
def LLM_CATCH_EXCEPTION(f):
"""
@ -384,6 +391,6 @@ def predict(inputs, llm_kwargs, *args, **kwargs):
additional_fn代表点击的哪个按钮按钮见functional.py
"""
method = model_info[llm_kwargs['llm_model']]["fn_with_ui"]
method = model_info[llm_kwargs['llm_model']]["fn_with_ui"] # 如果这里报错检查config中的AVAIL_LLM_MODELS选项
yield from method(inputs, llm_kwargs, *args, **kwargs)

View File

@ -1,241 +0,0 @@
"""
该文件中主要包含三个函数
不具备多线程能力的函数:
1. predict: 正常对话时使用,具备完备的交互功能,不可多线程
具备多线程调用能力的函数
2. predict_no_ui高级实验性功能模块调用不会实时显示在界面上参数简单可以多线程并行方便实现复杂的功能逻辑
3. predict_no_ui_long_connection在实验过程中发现调用predict_no_ui处理长文档时和openai的连接容易断掉这个函数用stream的方式解决这个问题同样支持多线程
"""
import logging
import traceback
import importlib
import openai
import time
# 读取config.py文件中关于AZURE OPENAI API的信息
from toolbox import get_conf, update_ui, clip_history, trimmed_format_exc
TIMEOUT_SECONDS, MAX_RETRY, AZURE_ENGINE, AZURE_ENDPOINT, AZURE_API_VERSION, AZURE_API_KEY = \
get_conf('TIMEOUT_SECONDS', 'MAX_RETRY',"AZURE_ENGINE","AZURE_ENDPOINT", "AZURE_API_VERSION", "AZURE_API_KEY")
def get_full_error(chunk, stream_response):
"""
获取完整的从Openai返回的报错
"""
while True:
try:
chunk += next(stream_response)
except:
break
return chunk
def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_prompt='', stream = True, additional_fn=None):
"""
发送至azure openai api流式获取输出。
用于基础的对话功能。
inputs 是本次问询的输入
top_p, temperature是chatGPT的内部调优参数
history 是之前的对话列表注意无论是inputs还是history内容太长了都会触发token数量溢出的错误
chatbot 为WebUI中显示的对话列表修改它然后yeild出去可以直接修改对话界面内容
additional_fn代表点击的哪个按钮按钮见functional.py
"""
print(llm_kwargs["llm_model"])
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"]
raw_input = inputs
logging.info(f'[raw_input] {raw_input}')
chatbot.append((inputs, ""))
yield from update_ui(chatbot=chatbot, history=history, msg="等待响应") # 刷新界面
payload = generate_azure_payload(inputs, llm_kwargs, history, system_prompt, stream)
history.append(inputs); history.append("")
retry = 0
while True:
try:
openai.api_type = "azure"
openai.api_version = AZURE_API_VERSION
openai.api_base = AZURE_ENDPOINT
openai.api_key = AZURE_API_KEY
response = openai.ChatCompletion.create(timeout=TIMEOUT_SECONDS, **payload);break
except:
retry += 1
chatbot[-1] = ((chatbot[-1][0], "获取response失败重试中。。。"))
retry_msg = f",正在重试 ({retry}/{MAX_RETRY}) ……" if MAX_RETRY > 0 else ""
yield from update_ui(chatbot=chatbot, history=history, msg="请求超时"+retry_msg) # 刷新界面
if retry > MAX_RETRY: raise TimeoutError
gpt_replying_buffer = ""
is_head_of_the_stream = True
if stream:
stream_response = response
while True:
try:
chunk = next(stream_response)
except StopIteration:
from toolbox import regular_txt_to_markdown; tb_str = '```\n' + trimmed_format_exc() + '```'
chatbot[-1] = (chatbot[-1][0], f"[Local Message] 远程返回错误: \n\n{tb_str} \n\n{regular_txt_to_markdown(chunk)}")
yield from update_ui(chatbot=chatbot, history=history, msg="远程返回错误:" + chunk) # 刷新界面
return
if is_head_of_the_stream and (r'"object":"error"' not in chunk):
# 数据流的第一帧不携带content
is_head_of_the_stream = False; continue
if chunk:
#print(chunk)
try:
if "delta" in chunk["choices"][0]:
if chunk["choices"][0]["finish_reason"] == "stop":
logging.info(f'[response] {gpt_replying_buffer}')
break
status_text = f"finish_reason: {chunk['choices'][0]['finish_reason']}"
gpt_replying_buffer = gpt_replying_buffer + chunk["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) # 刷新界面
except Exception as e:
traceback.print_exc()
yield from update_ui(chatbot=chatbot, history=history, msg="Json解析不合常规") # 刷新界面
chunk = get_full_error(chunk, stream_response)
error_msg = chunk
yield from update_ui(chatbot=chatbot, history=history, msg="Json异常" + error_msg) # 刷新界面
return
def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="", observe_window=None, console_slience=False):
"""
发送至AZURE OPENAI API等待回复一次性完成不显示中间过程。但内部用stream的方法避免中途网线被掐。
inputs
是本次问询的输入
sys_prompt:
系统静默prompt
llm_kwargs
chatGPT的内部调优参数
history
是之前的对话列表
observe_window = None
用于负责跨越线程传递已经输出的部分大部分时候仅仅为了fancy的视觉效果留空即可。observe_window[0]观测窗。observe_window[1]:看门狗
"""
watch_dog_patience = 5 # 看门狗的耐心, 设置5秒即可
payload = generate_azure_payload(inputs, llm_kwargs, history, system_prompt=sys_prompt, stream=True)
retry = 0
while True:
try:
openai.api_type = "azure"
openai.api_version = AZURE_API_VERSION
openai.api_base = AZURE_ENDPOINT
openai.api_key = AZURE_API_KEY
response = openai.ChatCompletion.create(timeout=TIMEOUT_SECONDS, **payload);break
except:
retry += 1
traceback.print_exc()
if retry > MAX_RETRY: raise TimeoutError
if MAX_RETRY!=0: print(f'请求超时,正在重试 ({retry}/{MAX_RETRY}) ……')
stream_response = response
result = ''
while True:
try: chunk = next(stream_response)
except StopIteration:
break
except:
chunk = next(stream_response) # 失败了,重试一次?再失败就没办法了。
if len(chunk)==0: continue
if not chunk.startswith('data:'):
error_msg = get_full_error(chunk, stream_response)
if "reduce the length" in error_msg:
raise ConnectionAbortedError("AZURE OPENAI API拒绝了请求:" + error_msg)
else:
raise RuntimeError("AZURE OPENAI API拒绝了请求" + error_msg)
if ('data: [DONE]' in chunk): break
delta = chunk["delta"]
if len(delta) == 0: break
if "role" in delta: continue
if "content" in delta:
result += delta["content"]
if not console_slience: print(delta["content"], end='')
if observe_window is not None:
# 观测窗,把已经获取的数据显示出去
if len(observe_window) >= 1: observe_window[0] += delta["content"]
# 看门狗,如果超过期限没有喂狗,则终止
if len(observe_window) >= 2:
if (time.time()-observe_window[1]) > watch_dog_patience:
raise RuntimeError("用户取消了程序。")
else: raise RuntimeError("意外Json结构"+delta)
if chunk['finish_reason'] == 'length':
raise ConnectionAbortedError("正常结束但显示Token不足导致输出不完整请削减单次输入的文本量。")
return result
def generate_azure_payload(inputs, llm_kwargs, history, system_prompt, stream):
"""
整合所有信息选择LLM模型生成 azure openai api请求为发送请求做准备
"""
conversation_cnt = len(history) // 2
messages = [{"role": "system", "content": system_prompt}]
if conversation_cnt:
for index in range(0, 2*conversation_cnt, 2):
what_i_have_asked = {}
what_i_have_asked["role"] = "user"
what_i_have_asked["content"] = history[index]
what_gpt_answer = {}
what_gpt_answer["role"] = "assistant"
what_gpt_answer["content"] = history[index+1]
if what_i_have_asked["content"] != "":
if what_gpt_answer["content"] == "": continue
messages.append(what_i_have_asked)
messages.append(what_gpt_answer)
else:
messages[-1]['content'] = what_gpt_answer['content']
what_i_ask_now = {}
what_i_ask_now["role"] = "user"
what_i_ask_now["content"] = inputs
messages.append(what_i_ask_now)
payload = {
"model": llm_kwargs['llm_model'],
"messages": messages,
"temperature": llm_kwargs['temperature'], # 1.0,
"top_p": llm_kwargs['top_p'], # 1.0,
"n": 1,
"stream": stream,
"presence_penalty": 0,
"frequency_penalty": 0,
"engine": AZURE_ENGINE
}
try:
print(f" {llm_kwargs['llm_model']} : {conversation_cnt} : {inputs[:100]} ..........")
except:
print('输入中可能存在乱码。')
return payload

View File

@ -0,0 +1,210 @@
from transformers import AutoModel, AutoTokenizer
import time
import os
import json
import threading
import importlib
from toolbox import update_ui, get_conf
from multiprocessing import Process, Pipe
load_message = "ChatGLMFT尚未加载加载需要一段时间。注意取决于`config.py`的配置ChatGLMFT消耗大量的内存CPU或显存GPU也许会导致低配计算机卡死 ……"
def string_to_options(arguments):
import argparse
import shlex
# Create an argparse.ArgumentParser instance
parser = argparse.ArgumentParser()
# Add command-line arguments
parser.add_argument("--llm_to_learn", type=str, help="LLM model to learn", default="gpt-3.5-turbo")
parser.add_argument("--prompt_prefix", type=str, help="Prompt prefix", default='')
parser.add_argument("--system_prompt", type=str, help="System prompt", default='')
parser.add_argument("--batch", type=int, help="System prompt", default=50)
# Parse the arguments
args = parser.parse_args(shlex.split(arguments))
return args
#################################################################################
class GetGLMFTHandle(Process):
def __init__(self):
super().__init__(daemon=True)
self.parent, self.child = Pipe()
self.chatglmft_model = None
self.chatglmft_tokenizer = None
self.info = ""
self.success = True
self.check_dependency()
self.start()
self.threadLock = threading.Lock()
def check_dependency(self):
try:
import sentencepiece
self.info = "依赖检测通过"
self.success = True
except:
self.info = "缺少ChatGLMFT的依赖如果要使用ChatGLMFT除了基础的pip依赖以外您还需要运行`pip install -r request_llm/requirements_chatglm.txt`安装ChatGLM的依赖。"
self.success = False
def ready(self):
return self.chatglmft_model is not None
def run(self):
# 子进程执行
# 第一次运行,加载参数
retry = 0
while True:
try:
if self.chatglmft_model is None:
from transformers import AutoConfig
import torch
# conf = 'request_llm/current_ptune_model.json'
# if not os.path.exists(conf): raise RuntimeError('找不到微调模型信息')
# with open(conf, 'r', encoding='utf8') as f:
# model_args = json.loads(f.read())
ChatGLM_PTUNING_CHECKPOINT, = get_conf('ChatGLM_PTUNING_CHECKPOINT')
assert os.path.exists(ChatGLM_PTUNING_CHECKPOINT), "找不到微调模型检查点"
conf = os.path.join(ChatGLM_PTUNING_CHECKPOINT, "config.json")
with open(conf, 'r', encoding='utf8') as f:
model_args = json.loads(f.read())
if 'model_name_or_path' not in model_args:
model_args['model_name_or_path'] = model_args['_name_or_path']
self.chatglmft_tokenizer = AutoTokenizer.from_pretrained(
model_args['model_name_or_path'], trust_remote_code=True)
config = AutoConfig.from_pretrained(
model_args['model_name_or_path'], trust_remote_code=True)
config.pre_seq_len = model_args['pre_seq_len']
config.prefix_projection = model_args['prefix_projection']
print(f"Loading prefix_encoder weight from {ChatGLM_PTUNING_CHECKPOINT}")
model = AutoModel.from_pretrained(model_args['model_name_or_path'], config=config, trust_remote_code=True)
prefix_state_dict = torch.load(os.path.join(ChatGLM_PTUNING_CHECKPOINT, "pytorch_model.bin"))
new_prefix_state_dict = {}
for k, v in prefix_state_dict.items():
if k.startswith("transformer.prefix_encoder."):
new_prefix_state_dict[k[len("transformer.prefix_encoder."):]] = v
model.transformer.prefix_encoder.load_state_dict(new_prefix_state_dict)
if model_args['quantization_bit'] is not None:
print(f"Quantized to {model_args['quantization_bit']} bit")
model = model.quantize(model_args['quantization_bit'])
model = model.cuda()
if model_args['pre_seq_len'] is not None:
# P-tuning v2
model.transformer.prefix_encoder.float()
self.chatglmft_model = model.eval()
break
else:
break
except Exception as e:
retry += 1
if retry > 3:
self.child.send('[Local Message] Call ChatGLMFT fail 不能正常加载ChatGLMFT的参数。')
raise RuntimeError("不能正常加载ChatGLMFT的参数")
while True:
# 进入任务等待状态
kwargs = self.child.recv()
# 收到消息,开始请求
try:
for response, history in self.chatglmft_model.stream_chat(self.chatglmft_tokenizer, **kwargs):
self.child.send(response)
# # 中途接收可能的终止指令(如果有的话)
# if self.child.poll():
# command = self.child.recv()
# if command == '[Terminate]': break
except:
from toolbox import trimmed_format_exc
self.child.send('[Local Message] Call ChatGLMFT fail.' + '\n```\n' + trimmed_format_exc() + '\n```\n')
# 请求处理结束,开始下一个循环
self.child.send('[Finish]')
def stream_chat(self, **kwargs):
# 主进程执行
self.threadLock.acquire()
self.parent.send(kwargs)
while True:
res = self.parent.recv()
if res != '[Finish]':
yield res
else:
break
self.threadLock.release()
global glmft_handle
glmft_handle = None
#################################################################################
def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="", observe_window=[], console_slience=False):
"""
多线程方法
函数的说明请见 request_llm/bridge_all.py
"""
global glmft_handle
if glmft_handle is None:
glmft_handle = GetGLMFTHandle()
if len(observe_window) >= 1: observe_window[0] = load_message + "\n\n" + glmft_handle.info
if not glmft_handle.success:
error = glmft_handle.info
glmft_handle = None
raise RuntimeError(error)
# chatglmft 没有 sys_prompt 接口因此把prompt加入 history
history_feedin = []
history_feedin.append(["What can I do?", sys_prompt])
for i in range(len(history)//2):
history_feedin.append([history[2*i], history[2*i+1]] )
watch_dog_patience = 5 # 看门狗 (watchdog) 的耐心, 设置5秒即可
response = ""
for response in glmft_handle.stream_chat(query=inputs, history=history_feedin, max_length=llm_kwargs['max_length'], top_p=llm_kwargs['top_p'], temperature=llm_kwargs['temperature']):
if len(observe_window) >= 1: 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 glmft_handle
if glmft_handle is None:
glmft_handle = GetGLMFTHandle()
chatbot[-1] = (inputs, load_message + "\n\n" + glmft_handle.info)
yield from update_ui(chatbot=chatbot, history=[])
if not glmft_handle.success:
glmft_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 = []
history_feedin.append(["What can I do?", system_prompt] )
for i in range(len(history)//2):
history_feedin.append([history[2*i], history[2*i+1]] )
# 开始接收chatglmft的回复
response = "[Local Message]: 等待ChatGLMFT响应中 ..."
for response in glmft_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)
# 总结输出
if response == "[Local Message]: 等待ChatGLMFT响应中 ...":
response = "[Local Message]: ChatGLMFT响应异常 ..."
history.extend([inputs, response])
yield from update_ui(chatbot=chatbot, history=history)

View File

@ -12,20 +12,18 @@
"""
import json
import random
import time
import gradio as gr
import logging
import traceback
import requests
import importlib
import func_box
# config_private.py放自己的秘密如API和代理网址
# 读取时首先看是否存在私密的config_private配置文件不受git管控如果有则覆盖原config文件
from toolbox import get_conf, update_ui, is_any_api_key, select_api_key, what_keys, clip_history, trimmed_format_exc
proxies, API_KEY, TIMEOUT_SECONDS, MAX_RETRY = \
get_conf('proxies', 'API_KEY', 'TIMEOUT_SECONDS', 'MAX_RETRY')
proxies, TIMEOUT_SECONDS, MAX_RETRY, API_ORG = \
get_conf('proxies', 'TIMEOUT_SECONDS', 'MAX_RETRY', 'API_ORG')
timeout_bot_msg = '[Local Message] Request timeout. Network error. Please check proxy settings in config.py.' + \
'网络错误,检查代理服务器是否可用,以及代理设置的格式是否正确,格式须是[协议]://[地址]:[端口],缺一不可。'
@ -62,7 +60,7 @@ 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
from request_llm.bridge_all import model_info
from .bridge_all import model_info
endpoint = model_info[llm_kwargs['llm_model']]['endpoint']
response = requests.post(endpoint, headers=headers, proxies=proxies,
json=payload, stream=True, timeout=TIMEOUT_SECONDS); break
@ -103,12 +101,14 @@ def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="",
if (time.time()-observe_window[1]) > watch_dog_patience:
raise RuntimeError("用户取消了程序。")
else: raise RuntimeError("意外Json结构"+delta)
if json_data['finish_reason'] == 'content_filter':
raise RuntimeError("由于提问含不合规内容被Azure过滤。")
if json_data['finish_reason'] == 'length':
raise ConnectionAbortedError("正常结束但显示Token不足导致输出不完整请削减单次输入的文本量。")
return result
def predict(inputs, llm_kwargs, plugin_kwargs, 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流式获取输出。
用于基础的对话功能。
@ -136,22 +136,24 @@ def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_promp
inputs = core_functional[additional_fn]["Prefix"] + inputs + core_functional[additional_fn]["Suffix"]
raw_input = inputs
logging.info(f'[raw_input]_{llm_kwargs["ipaddr"]} {raw_input}')
logging.info(f'[raw_input] {raw_input}')
chatbot.append((inputs, ""))
loading_msg = func_box.spinner_chatbot_loading(chatbot)
yield from update_ui(chatbot=loading_msg, history=history, msg="等待响应") # 刷新界面
yield from update_ui(chatbot=chatbot, history=history, msg="等待响应") # 刷新界面
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
from request_llm.bridge_all import model_info
from .bridge_all import model_info
endpoint = model_info[llm_kwargs['llm_model']]['endpoint']
response = requests.post(endpoint, headers=headers, proxies=proxies,
json=payload, stream=True, timeout=TIMEOUT_SECONDS);break
@ -163,6 +165,7 @@ def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_promp
if retry > MAX_RETRY: raise TimeoutError
gpt_replying_buffer = ""
is_head_of_the_stream = True
if stream:
stream_response = response.iter_lines()
@ -180,44 +183,47 @@ def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_promp
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:
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]_{llm_kwargs["ipaddr"]} {gpt_replying_buffer}')
logging.info(f'[response] {gpt_replying_buffer}')
break
# 处理数据流的主体
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_decoded[6:])['choices'][0]["delta"]["content"]
history[-1] = gpt_replying_buffer
chatbot[-1] = (history[-2], history[-1])
count_time = round(time.time() - llm_kwargs['start_time'], 3)
status_text = f"finish_reason: {chunkjson['choices'][0]['finish_reason']}\t" \
f"本次对话耗时: {func_box.html_tag_color(tag=f'{count_time}s')}"
yield from update_ui(chatbot=chatbot, history=history, msg=status_text) # 刷新界面
except Exception as e:
traceback.print_exc()
yield from update_ui(chatbot=chatbot, history=history, msg="Json解析不合常规") # 刷新界面
chunk = get_full_error(chunk, stream_response)
chunk_decoded = chunk.decode()
error_msg = chunk_decoded
openai_website = ' 请登录OpenAI查看详情 https://platform.openai.com/signup'
if "reduce the length" in error_msg:
if len(history) >= 2: history[-1] = ""; history[-2] = "" # 清除当前溢出的输入history[-2] 是本次输入, history[-1] 是本次输出
history = clip_history(inputs=inputs, history=history, tokenizer=model_info[llm_kwargs['llm_model']]['tokenizer'],
history = clip_history(inputs=inputs, history=history, tokenizer=model_info[llm_kwargs['llm_model']]['tokenizer'],
max_token_limit=(model_info[llm_kwargs['llm_model']]['max_token'])) # history至少释放二分之一
chatbot[-1] = (chatbot[-1][0], "[Local Message] Reduce the length. 本次输入过长, 或历史数据过长. 历史缓存数据已部分释放, 您可以请再次尝试. (若再次失败则更可能是因为输入过长.)")
# history = [] # 清除历史
elif "does not exist" in error_msg:
chatbot[-1] = (chatbot[-1][0], f"[Local Message] Model {llm_kwargs['llm_model']} does not exist. 模型不存在, 或者您没有获得体验资格.")
elif "Incorrect API key" in error_msg:
chatbot[-1] = (chatbot[-1][0], "[Local Message] Incorrect API key. OpenAI以提供了不正确的API_KEY为由, 拒绝服务.")
chatbot[-1] = (chatbot[-1][0], "[Local Message] Incorrect API key. OpenAI以提供了不正确的API_KEY为由, 拒绝服务. " + openai_website)
elif "exceeded your current quota" in error_msg:
chatbot[-1] = (chatbot[-1][0], "[Local Message] You exceeded your current quota. OpenAI以账户额度不足为由, 拒绝服务.")
chatbot[-1] = (chatbot[-1][0], "[Local Message] You exceeded your current quota. OpenAI以账户额度不足为由, 拒绝服务." + openai_website)
elif "account is not active" in error_msg:
chatbot[-1] = (chatbot[-1][0], "[Local Message] Your account is not active. OpenAI以账户失效为由, 拒绝服务." + openai_website)
elif "associated with a deactivated account" in error_msg:
chatbot[-1] = (chatbot[-1][0], "[Local Message] You are associated with a deactivated account. OpenAI以账户失效为由, 拒绝服务." + openai_website)
elif "bad forward key" in error_msg:
chatbot[-1] = (chatbot[-1][0], "[Local Message] Bad forward key. API2D账户额度不足.")
elif "Not enough point" in error_msg:
@ -228,9 +234,6 @@ def predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history=[], system_promp
chatbot[-1] = (chatbot[-1][0], f"[Local Message] 异常 \n\n{tb_str} \n\n{regular_txt_to_markdown(chunk_decoded)}")
yield from update_ui(chatbot=chatbot, history=history, msg="Json异常" + error_msg) # 刷新界面
return
count_tokens = func_box.num_tokens_from_string(listing=history)
status_text += f'\t 本次对话使用tokens: {func_box.html_tag_color(count_tokens)}'
yield from update_ui(chatbot=chatbot, history=history, msg=status_text) # 刷新界面
def generate_payload(inputs, llm_kwargs, history, system_prompt, stream):
"""
@ -238,18 +241,15 @@ def generate_payload(inputs, llm_kwargs, history, system_prompt, stream):
"""
if not is_any_api_key(llm_kwargs['api_key']):
raise AssertionError("你提供了错误的API_KEY。\n\n1. 临时解决方案直接在输入区键入api_key然后回车提交。\n\n2. 长效解决方案在config.py中配置。")
if llm_kwargs['llm_model'].startswith('proxy-'):
api_key = select_api_key(llm_kwargs['api_key'], llm_kwargs['llm_model'])
headers = {
"Content-Type": "application/json",
"api-key": f"{api_key}"
}
else:
api_key = select_api_key(llm_kwargs['api_key'], llm_kwargs['llm_model'])
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}
api_key = select_api_key(llm_kwargs['api_key'], llm_kwargs['llm_model'])
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}
if API_ORG.startswith('org-'): headers.update({"OpenAI-Organization": API_ORG})
if llm_kwargs['llm_model'].startswith('azure-'): headers.update({"api-key": api_key})
conversation_cnt = len(history) // 2
@ -286,20 +286,9 @@ def generate_payload(inputs, llm_kwargs, history, system_prompt, stream):
"frequency_penalty": 0,
}
try:
print("\033[1;35m", f"{llm_kwargs['llm_model']}_{llm_kwargs['ipaddr']} :", "\033[0m", f"{conversation_cnt} : {inputs[:100]} ..........")
print(f" {llm_kwargs['llm_model']} : {conversation_cnt} : {inputs[:100]} ..........")
except:
print('输入中可能存在乱码。')
return headers, payload
return headers,payload
if __name__ == '__main__':
llm_kwargs = {
'api_key': 'sk-',
'llm_model': 'gpt-3.5-turbo',
'top_p': 1,
'max_length': 512,
'temperature': 1,
# 'ipaddr': ipaddr.client.host
}
chat = []
predict('你好', llm_kwargs=llm_kwargs, chatbot=chat, plugin_kwargs={})
print(chat)

View File

@ -1,254 +0,0 @@
"""
========================================================================
第一部分来自EdgeGPT.py
https://github.com/acheong08/EdgeGPT
========================================================================
"""
from .edge_gpt import NewbingChatbot
load_message = "等待NewBing响应。"
"""
========================================================================
第二部分子进程Worker调用主体
========================================================================
"""
import time
import json
import re
import logging
import asyncio
import importlib
import threading
from toolbox import update_ui, get_conf, trimmed_format_exc
from multiprocessing import Process, Pipe
def preprocess_newbing_out(s):
pattern = r'\^(\d+)\^' # 匹配^数字^
sub = lambda m: '('+m.group(1)+')' # 将匹配到的数字作为替换值
result = re.sub(pattern, sub, s) # 替换操作
if '[1]' in result:
result += '\n\n```reference\n' + "\n".join([r for r in result.split('\n') if r.startswith('[')]) + '\n```\n'
return result
def preprocess_newbing_out_simple(result):
if '[1]' in result:
result += '\n\n```reference\n' + "\n".join([r for r in result.split('\n') if r.startswith('[')]) + '\n```\n'
return result
class NewBingHandle(Process):
def __init__(self):
super().__init__(daemon=True)
self.parent, self.child = Pipe()
self.newbing_model = None
self.info = ""
self.success = True
self.local_history = []
self.check_dependency()
self.start()
self.threadLock = threading.Lock()
def check_dependency(self):
try:
self.success = False
import certifi, httpx, rich
self.info = "依赖检测通过等待NewBing响应。注意目前不能多人同时调用NewBing接口有线程锁否则将导致每个人的NewBing问询历史互相渗透。调用NewBing时会自动使用已配置的代理。"
self.success = True
except:
self.info = "缺少的依赖如果要使用Newbing除了基础的pip依赖以外您还需要运行`pip install -r request_llm/requirements_newbing.txt`安装Newbing的依赖。"
self.success = False
def ready(self):
return self.newbing_model is not None
async def async_run(self):
# 读取配置
NEWBING_STYLE, = get_conf('NEWBING_STYLE')
from request_llm.bridge_all import model_info
endpoint = model_info['newbing']['endpoint']
while True:
# 等待
kwargs = self.child.recv()
question=kwargs['query']
history=kwargs['history']
system_prompt=kwargs['system_prompt']
# 是否重置
if len(self.local_history) > 0 and len(history)==0:
await self.newbing_model.reset()
self.local_history = []
# 开始问问题
prompt = ""
if system_prompt not in self.local_history:
self.local_history.append(system_prompt)
prompt += system_prompt + '\n'
# 追加历史
for ab in history:
a, b = ab
if a not in self.local_history:
self.local_history.append(a)
prompt += a + '\n'
# if b not in self.local_history:
# self.local_history.append(b)
# prompt += b + '\n'
# 问题
prompt += question
self.local_history.append(question)
print('question:', prompt)
# 提交
async for final, response in self.newbing_model.ask_stream(
prompt=question,
conversation_style=NEWBING_STYLE, # ["creative", "balanced", "precise"]
wss_link=endpoint, # "wss://sydney.bing.com/sydney/ChatHub"
):
if not final:
print(response)
self.child.send(str(response))
else:
print('-------- receive final ---------')
self.child.send('[Finish]')
# self.local_history.append(response)
def run(self):
"""
这个函数运行在子进程
"""
# 第一次运行,加载参数
self.success = False
self.local_history = []
if (self.newbing_model is None) or (not self.success):
# 代理设置
proxies, = get_conf('proxies')
if proxies is None:
self.proxies_https = None
else:
self.proxies_https = proxies['https']
# cookie
NEWBING_COOKIES, = get_conf('NEWBING_COOKIES')
try:
cookies = json.loads(NEWBING_COOKIES)
except:
self.success = False
tb_str = '\n```\n' + trimmed_format_exc() + '\n```\n'
self.child.send(f'[Local Message] 不能加载Newbing组件。NEWBING_COOKIES未填写或有格式错误。')
self.child.send('[Fail]')
self.child.send('[Finish]')
raise RuntimeError(f"不能加载Newbing组件。NEWBING_COOKIES未填写或有格式错误。")
try:
self.newbing_model = NewbingChatbot(proxy=self.proxies_https, cookies=cookies)
except:
self.success = False
tb_str = '\n```\n' + trimmed_format_exc() + '\n```\n'
self.child.send(f'[Local Message] 不能加载Newbing组件。{tb_str}')
self.child.send('[Fail]')
self.child.send('[Finish]')
raise RuntimeError(f"不能加载Newbing组件。")
self.success = True
try:
# 进入任务等待状态
asyncio.run(self.async_run())
except Exception:
tb_str = '\n```\n' + trimmed_format_exc() + '\n```\n'
self.child.send(f'[Local Message] Newbing失败 {tb_str}.')
self.child.send('[Fail]')
self.child.send('[Finish]')
def stream_chat(self, **kwargs):
"""
这个函数运行在主进程
"""
self.threadLock.acquire()
self.parent.send(kwargs) # 发送请求到子进程
while True:
res = self.parent.recv() # 等待newbing回复的片段
if res == '[Finish]':
break # 结束
elif res == '[Fail]':
self.success = False
break
else:
yield res # newbing回复的片段
self.threadLock.release()
"""
========================================================================
第三部分:主进程统一调用函数接口
========================================================================
"""
global newbing_handle
newbing_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 newbing_handle
if (newbing_handle is None) or (not newbing_handle.success):
newbing_handle = NewBingHandle()
observe_window[0] = load_message + "\n\n" + newbing_handle.info
if not newbing_handle.success:
error = newbing_handle.info
newbing_handle = None
raise RuntimeError(error)
# 没有 sys_prompt 接口因此把prompt加入 history
history_feedin = []
for i in range(len(history)//2):
history_feedin.append([history[2*i], history[2*i+1]] )
watch_dog_patience = 5 # 看门狗 (watchdog) 的耐心, 设置5秒即可
response = ""
observe_window[0] = "[Local Message]: 等待NewBing响应中 ..."
for response in newbing_handle.stream_chat(query=inputs, history=history_feedin, system_prompt=sys_prompt, max_length=llm_kwargs['max_length'], top_p=llm_kwargs['top_p'], temperature=llm_kwargs['temperature']):
observe_window[0] = preprocess_newbing_out_simple(response)
if len(observe_window) >= 2:
if (time.time()-observe_window[1]) > watch_dog_patience:
raise RuntimeError("程序终止。")
return preprocess_newbing_out_simple(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, "[Local Message]: 等待NewBing响应中 ..."))
global newbing_handle
if (newbing_handle is None) or (not newbing_handle.success):
newbing_handle = NewBingHandle()
chatbot[-1] = (inputs, load_message + "\n\n" + newbing_handle.info)
yield from update_ui(chatbot=chatbot, history=[])
if not newbing_handle.success:
newbing_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([history[2*i], history[2*i+1]] )
chatbot[-1] = (inputs, "[Local Message]: 等待NewBing响应中 ...")
response = "[Local Message]: 等待NewBing响应中 ..."
yield from update_ui(chatbot=chatbot, history=history, msg="NewBing响应缓慢尚未完成全部响应请耐心完成后再提交新问题。")
for response in newbing_handle.stream_chat(query=inputs, history=history_feedin, system_prompt=system_prompt, max_length=llm_kwargs['max_length'], top_p=llm_kwargs['top_p'], temperature=llm_kwargs['temperature']):
chatbot[-1] = (inputs, preprocess_newbing_out(response))
yield from update_ui(chatbot=chatbot, history=history, msg="NewBing响应缓慢尚未完成全部响应请耐心完成后再提交新问题。")
if response == "[Local Message]: 等待NewBing响应中 ...": response = "[Local Message]: NewBing响应异常请刷新界面重试 ..."
history.extend([inputs, response])
logging.info(f'[raw_input] {inputs}')
logging.info(f'[response] {response}')
yield from update_ui(chatbot=chatbot, history=history, msg="完成全部响应,请提交新问题。")

View File

@ -89,9 +89,6 @@ class NewBingHandle(Process):
if a not in self.local_history:
self.local_history.append(a)
prompt += a + '\n'
# if b not in self.local_history:
# self.local_history.append(b)
# prompt += b + '\n'
# 问题
prompt += question
@ -101,7 +98,7 @@ class NewBingHandle(Process):
async for final, response in self.newbing_model.ask_stream(
prompt=question,
conversation_style=NEWBING_STYLE, # ["creative", "balanced", "precise"]
wss_link=endpoint, # "wss://sydney.bing.com/sydney/ChatHub"
wss_link=endpoint, # "wss://sydney.bing.com/sydney/ChatHub"
):
if not final:
print(response)
@ -121,14 +118,26 @@ class NewBingHandle(Process):
self.local_history = []
if (self.newbing_model is None) or (not self.success):
# 代理设置
proxies, = get_conf('proxies')
proxies, NEWBING_COOKIES = get_conf('proxies', 'NEWBING_COOKIES')
if proxies is None:
self.proxies_https = None
else:
self.proxies_https = proxies['https']
if (NEWBING_COOKIES is not None) and len(NEWBING_COOKIES) > 100:
try:
cookies = json.loads(NEWBING_COOKIES)
except:
self.success = False
tb_str = '\n```\n' + trimmed_format_exc() + '\n```\n'
self.child.send(f'[Local Message] NEWBING_COOKIES未填写或有格式错误。')
self.child.send('[Fail]'); self.child.send('[Finish]')
raise RuntimeError(f"NEWBING_COOKIES未填写或有格式错误。")
else:
cookies = None
try:
self.newbing_model = NewbingChatbot(proxy=self.proxies_https)
self.newbing_model = NewbingChatbot(proxy=self.proxies_https, cookies=cookies)
except:
self.success = False
tb_str = '\n```\n' + trimmed_format_exc() + '\n```\n'
@ -143,7 +152,7 @@ class NewBingHandle(Process):
asyncio.run(self.async_run())
except Exception:
tb_str = '\n```\n' + trimmed_format_exc() + '\n```\n'
self.child.send(f'[Local Message] Newbing失败 {tb_str}.')
self.child.send(f'[Local Message] Newbing 请求失败,报错信息如下. 如果是与网络相关的问题建议更换代理协议推荐http或代理节点 {tb_str}.')
self.child.send('[Fail]')
self.child.send('[Finish]')
@ -151,18 +160,14 @@ class NewBingHandle(Process):
"""
这个函数运行在主进程
"""
self.threadLock.acquire()
self.parent.send(kwargs) # 发送请求子进程
self.threadLock.acquire() # 获取线程锁
self.parent.send(kwargs) # 请求子进程
while True:
res = self.parent.recv() # 等待newbing回复的片段
if res == '[Finish]':
break # 结束
elif res == '[Fail]':
self.success = False
break
else:
yield res # newbing回复的片段
self.threadLock.release()
res = self.parent.recv() # 等待newbing回复的片段
if res == '[Finish]': break # 结束
elif res == '[Fail]': self.success = False; break # 失败
else: yield res # newbing回复的片段
self.threadLock.release() # 释放线程锁
"""

View File

@ -1,4 +1,4 @@
from .bridge_newbing import preprocess_newbing_out, preprocess_newbing_out_simple
from .bridge_newbingfree import preprocess_newbing_out, preprocess_newbing_out_simple
from multiprocessing import Process, Pipe
from toolbox import update_ui, get_conf, trimmed_format_exc
import threading

View File

@ -1,409 +0,0 @@
"""
========================================================================
第一部分来自EdgeGPT.py
https://github.com/acheong08/EdgeGPT
========================================================================
"""
import argparse
import asyncio
import json
import os
import random
import re
import ssl
import sys
import uuid
from enum import Enum
from typing import Generator
from typing import Literal
from typing import Optional
from typing import Union
import websockets.client as websockets
DELIMITER = "\x1e"
# Generate random IP between range 13.104.0.0/14
FORWARDED_IP = (
f"13.{random.randint(104, 107)}.{random.randint(0, 255)}.{random.randint(0, 255)}"
)
HEADERS = {
"accept": "application/json",
"accept-language": "en-US,en;q=0.9",
"content-type": "application/json",
"sec-ch-ua": '"Not_A Brand";v="99", "Microsoft Edge";v="110", "Chromium";v="110"',
"sec-ch-ua-arch": '"x86"',
"sec-ch-ua-bitness": '"64"',
"sec-ch-ua-full-version": '"109.0.1518.78"',
"sec-ch-ua-full-version-list": '"Chromium";v="110.0.5481.192", "Not A(Brand";v="24.0.0.0", "Microsoft Edge";v="110.0.1587.69"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-model": "",
"sec-ch-ua-platform": '"Windows"',
"sec-ch-ua-platform-version": '"15.0.0"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-ms-client-request-id": str(uuid.uuid4()),
"x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/Win32",
"Referer": "https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx",
"Referrer-Policy": "origin-when-cross-origin",
"x-forwarded-for": FORWARDED_IP,
}
HEADERS_INIT_CONVER = {
"authority": "edgeservices.bing.com",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"accept-language": "en-US,en;q=0.9",
"cache-control": "max-age=0",
"sec-ch-ua": '"Chromium";v="110", "Not A(Brand";v="24", "Microsoft Edge";v="110"',
"sec-ch-ua-arch": '"x86"',
"sec-ch-ua-bitness": '"64"',
"sec-ch-ua-full-version": '"110.0.1587.69"',
"sec-ch-ua-full-version-list": '"Chromium";v="110.0.5481.192", "Not A(Brand";v="24.0.0.0", "Microsoft Edge";v="110.0.1587.69"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-model": '""',
"sec-ch-ua-platform": '"Windows"',
"sec-ch-ua-platform-version": '"15.0.0"',
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.69",
"x-edge-shopping-flag": "1",
"x-forwarded-for": FORWARDED_IP,
}
def get_ssl_context():
import certifi
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations(certifi.where())
return ssl_context
class NotAllowedToAccess(Exception):
pass
class ConversationStyle(Enum):
creative = "h3imaginative,clgalileo,gencontentv3"
balanced = "galileo"
precise = "h3precise,clgalileo"
CONVERSATION_STYLE_TYPE = Optional[
Union[ConversationStyle, Literal["creative", "balanced", "precise"]]
]
def _append_identifier(msg: dict) -> str:
"""
Appends special character to end of message to identify end of message
"""
# Convert dict to json string
return json.dumps(msg) + DELIMITER
def _get_ran_hex(length: int = 32) -> str:
"""
Returns random hex string
"""
return "".join(random.choice("0123456789abcdef") for _ in range(length))
class _ChatHubRequest:
"""
Request object for ChatHub
"""
def __init__(
self,
conversation_signature: str,
client_id: str,
conversation_id: str,
invocation_id: int = 0,
) -> None:
self.struct: dict = {}
self.client_id: str = client_id
self.conversation_id: str = conversation_id
self.conversation_signature: str = conversation_signature
self.invocation_id: int = invocation_id
def update(
self,
prompt,
conversation_style,
options,
) -> None:
"""
Updates request object
"""
if options is None:
options = [
"deepleo",
"enable_debug_commands",
"disable_emoji_spoken_text",
"enablemm",
]
if conversation_style:
if not isinstance(conversation_style, ConversationStyle):
conversation_style = getattr(ConversationStyle, conversation_style)
options = [
"nlu_direct_response_filter",
"deepleo",
"disable_emoji_spoken_text",
"responsible_ai_policy_235",
"enablemm",
conversation_style.value,
"dtappid",
"cricinfo",
"cricinfov2",
"dv3sugg",
]
self.struct = {
"arguments": [
{
"source": "cib",
"optionsSets": options,
"sliceIds": [
"222dtappid",
"225cricinfo",
"224locals0",
],
"traceId": _get_ran_hex(32),
"isStartOfSession": self.invocation_id == 0,
"message": {
"author": "user",
"inputMethod": "Keyboard",
"text": prompt,
"messageType": "Chat",
},
"conversationSignature": self.conversation_signature,
"participant": {
"id": self.client_id,
},
"conversationId": self.conversation_id,
},
],
"invocationId": str(self.invocation_id),
"target": "chat",
"type": 4,
}
self.invocation_id += 1
class _Conversation:
"""
Conversation API
"""
def __init__(
self,
cookies,
proxy,
) -> None:
self.struct: dict = {
"conversationId": None,
"clientId": None,
"conversationSignature": None,
"result": {"value": "Success", "message": None},
}
import httpx
self.proxy = proxy
proxy = (
proxy
or os.environ.get("all_proxy")
or os.environ.get("ALL_PROXY")
or os.environ.get("https_proxy")
or os.environ.get("HTTPS_PROXY")
or None
)
if proxy is not None and proxy.startswith("socks5h://"):
proxy = "socks5://" + proxy[len("socks5h://") :]
self.session = httpx.Client(
proxies=proxy,
timeout=30,
headers=HEADERS_INIT_CONVER,
)
for cookie in cookies:
self.session.cookies.set(cookie["name"], cookie["value"])
# Send GET request
response = self.session.get(
url=os.environ.get("BING_PROXY_URL")
or "https://edgeservices.bing.com/edgesvc/turing/conversation/create",
)
if response.status_code != 200:
response = self.session.get(
"https://edge.churchless.tech/edgesvc/turing/conversation/create",
)
if response.status_code != 200:
print(f"Status code: {response.status_code}")
print(response.text)
print(response.url)
raise Exception("Authentication failed")
try:
self.struct = response.json()
except (json.decoder.JSONDecodeError, NotAllowedToAccess) as exc:
raise Exception(
"Authentication failed. You have not been accepted into the beta.",
) from exc
if self.struct["result"]["value"] == "UnauthorizedRequest":
raise NotAllowedToAccess(self.struct["result"]["message"])
class _ChatHub:
"""
Chat API
"""
def __init__(self, conversation) -> None:
self.wss = None
self.request: _ChatHubRequest
self.loop: bool
self.task: asyncio.Task
print(conversation.struct)
self.request = _ChatHubRequest(
conversation_signature=conversation.struct["conversationSignature"],
client_id=conversation.struct["clientId"],
conversation_id=conversation.struct["conversationId"],
)
async def ask_stream(
self,
prompt: str,
wss_link: str,
conversation_style: CONVERSATION_STYLE_TYPE = None,
raw: bool = False,
options: dict = None,
) -> Generator[str, None, None]:
"""
Ask a question to the bot
"""
if self.wss and not self.wss.closed:
await self.wss.close()
# Check if websocket is closed
self.wss = await websockets.connect(
wss_link,
extra_headers=HEADERS,
max_size=None,
ssl=get_ssl_context()
)
await self._initial_handshake()
# Construct a ChatHub request
self.request.update(
prompt=prompt,
conversation_style=conversation_style,
options=options,
)
# Send request
await self.wss.send(_append_identifier(self.request.struct))
final = False
while not final:
objects = str(await self.wss.recv()).split(DELIMITER)
for obj in objects:
if obj is None or not obj:
continue
response = json.loads(obj)
if response.get("type") != 2 and raw:
yield False, response
elif response.get("type") == 1 and response["arguments"][0].get(
"messages",
):
resp_txt = response["arguments"][0]["messages"][0]["adaptiveCards"][
0
]["body"][0].get("text")
yield False, resp_txt
elif response.get("type") == 2:
final = True
yield True, response
async def _initial_handshake(self) -> None:
await self.wss.send(_append_identifier({"protocol": "json", "version": 1}))
await self.wss.recv()
async def close(self) -> None:
"""
Close the connection
"""
if self.wss and not self.wss.closed:
await self.wss.close()
class NewbingChatbot:
"""
Combines everything to make it seamless
"""
def __init__(
self,
cookies,
proxy
) -> None:
if cookies is None:
cookies = {}
self.cookies = cookies
self.proxy = proxy
self.chat_hub: _ChatHub = _ChatHub(
_Conversation(self.cookies, self.proxy),
)
async def ask(
self,
prompt: str,
wss_link: str,
conversation_style: CONVERSATION_STYLE_TYPE = None,
options: dict = None,
) -> dict:
"""
Ask a question to the bot
"""
async for final, response in self.chat_hub.ask_stream(
prompt=prompt,
conversation_style=conversation_style,
wss_link=wss_link,
options=options,
):
if final:
return response
await self.chat_hub.wss.close()
return None
async def ask_stream(
self,
prompt: str,
wss_link: str,
conversation_style: CONVERSATION_STYLE_TYPE = None,
raw: bool = False,
options: dict = None,
) -> Generator[str, None, None]:
"""
Ask a question to the bot
"""
async for response in self.chat_hub.ask_stream(
prompt=prompt,
conversation_style=conversation_style,
wss_link=wss_link,
raw=raw,
options=options,
):
yield response
async def close(self) -> None:
"""
Close the connection
"""
await self.chat_hub.close()
async def reset(self) -> None:
"""
Reset the conversation
"""
await self.close()
self.chat_hub = _ChatHub(_Conversation(self.cookies, self.proxy))

View File

@ -1,4 +1,5 @@
./docs/gradio-3.32.2-py3-none-any.whl
pydantic==1.10.11
tiktoken>=0.3.3
requests[socks]
transformers
@ -15,11 +16,4 @@ pymupdf
openai
numpy
arxiv
pymupdf
pyperclip
scikit-learn
psutil
distro
python-dotenv
rich
Levenshtein

47
theme/common.js Normal file
View File

@ -0,0 +1,47 @@
function ChatBotHeight() {
function update_height(){
var { panel_height_target, chatbot_height, chatbot } = get_elements();
if (panel_height_target!=chatbot_height)
{
var pixelString = panel_height_target.toString() + 'px';
chatbot.style.maxHeight = pixelString; chatbot.style.height = pixelString;
}
}
function update_height_slow(){
var { panel_height_target, chatbot_height, chatbot } = get_elements();
if (panel_height_target!=chatbot_height)
{
new_panel_height = (panel_height_target - chatbot_height)*0.5 + chatbot_height;
if (Math.abs(new_panel_height - panel_height_target) < 10){
new_panel_height = panel_height_target;
}
// console.log(chatbot_height, panel_height_target, new_panel_height);
var pixelString = new_panel_height.toString() + 'px';
chatbot.style.maxHeight = pixelString; chatbot.style.height = pixelString;
}
}
update_height();
setInterval(function() {
update_height_slow()
}, 50); // 每100毫秒执行一次
}
function get_elements() {
var chatbot = document.querySelector('#gpt-chatbot > div.wrap.svelte-18telvq');
if (!chatbot) {
chatbot = document.querySelector('#gpt-chatbot');
}
const panel1 = document.querySelector('#input-panel');
const panel2 = document.querySelector('#basic-panel');
const panel3 = document.querySelector('#plugin-panel');
const panel4 = document.querySelector('#interact-panel');
const panel5 = document.querySelector('#input-panel2');
const panel_active = document.querySelector('#state-panel');
var panel_height_target = (20-panel_active.offsetHeight) + panel1.offsetHeight + panel2.offsetHeight + panel3.offsetHeight + panel4.offsetHeight + panel5.offsetHeight + 21;
var panel_height_target = parseInt(panel_height_target);
var chatbot_height = chatbot.style.height;
var chatbot_height = parseInt(chatbot_height);
return { panel_height_target, chatbot_height, chatbot };
}

View File

@ -1,213 +1,28 @@
import gradio as gr
from toolbox import get_conf
CODE_HIGHLIGHT, ADD_WAIFU, ADD_CHUANHU = get_conf('CODE_HIGHLIGHT', 'ADD_WAIFU', 'ADD_CHUANHU')
# gradio可用颜色列表
# gr.themes.utils.colors.slate (石板色)
# gr.themes.utils.colors.gray (灰色)
# gr.themes.utils.colors.zinc (锌色)
# gr.themes.utils.colors.neutral (中性色)
# gr.themes.utils.colors.stone (石头色)
# gr.themes.utils.colors.red (红色)
# gr.themes.utils.colors.orange (橙色)
# gr.themes.utils.colors.amber (琥珀色)
# gr.themes.utils.colors.yellow (黄色)
# gr.themes.utils.colors.lime (酸橙色)
# gr.themes.utils.colors.green (绿色)
# gr.themes.utils.colors.emerald (祖母绿)
# gr.themes.utils.colors.teal (青蓝色)
# gr.themes.utils.colors.cyan (青色)
# gr.themes.utils.colors.sky (天蓝色)
# gr.themes.utils.colors.blue (蓝色)
# gr.themes.utils.colors.indigo (靛蓝色)
# gr.themes.utils.colors.violet (紫罗兰色)
# gr.themes.utils.colors.purple (紫色)
# gr.themes.utils.colors.fuchsia (洋红色)
# gr.themes.utils.colors.pink (粉红色)
# gr.themes.utils.colors.rose (玫瑰色)
def adjust_theme():
try:
set_theme = gr.themes.Soft(
primary_hue=gr.themes.Color(
c50="#EBFAF2",
c100="#CFF3E1",
c200="#A8EAC8",
c300="#77DEA9",
c400="#3FD086",
c500="#02C160",
c600="#06AE56",
c700="#05974E",
c800="#057F45",
c900="#04673D",
c950="#2E5541",
name="small_and_beautiful",
),
secondary_hue=gr.themes.Color(
c50="#576b95",
c100="#576b95",
c200="#576b95",
c300="#576b95",
c400="#576b95",
c500="#576b95",
c600="#576b95",
c700="#576b95",
c800="#576b95",
c900="#576b95",
c950="#576b95",
),
neutral_hue=gr.themes.Color(
name="gray",
c50="#f6f7f8",
# c100="#f3f4f6",
c100="#F2F2F2",
c200="#e5e7eb",
c300="#d1d5db",
c400="#B2B2B2",
c500="#808080",
c600="#636363",
c700="#515151",
c800="#393939",
# c900="#272727",
c900="#2B2B2B",
c950="#171717",
),
radius_size=gr.themes.sizes.radius_sm,
).set(
button_primary_background_fill="*primary_500",
button_primary_background_fill_dark="*primary_600",
button_primary_background_fill_hover="*primary_400",
button_primary_border_color="*primary_500",
button_primary_border_color_dark="*primary_600",
button_primary_text_color="wihte",
button_primary_text_color_dark="white",
button_secondary_background_fill="*neutral_100",
button_secondary_background_fill_hover="*neutral_50",
button_secondary_background_fill_dark="*neutral_900",
button_secondary_text_color="*neutral_800",
button_secondary_text_color_dark="white",
background_fill_primary="#F7F7F7",
background_fill_primary_dark="#1F1F1F",
block_title_text_color="*primary_500",
block_title_background_fill_dark="*primary_900",
block_label_background_fill_dark="*primary_900",
input_background_fill="#F6F6F6",
chatbot_code_background_color="*neutral_950",
chatbot_code_background_color_dark="*neutral_950",
)
js = ''
if ADD_CHUANHU:
with open("./docs/assets/custom.js", "r", encoding="utf-8") as f, \
open("./docs/assets/external-scripts.js", "r", encoding="utf-8") as f1:
customJS = f.read()
externalScripts = f1.read()
js += f'<script>{customJS}</script><script async>{externalScripts}</script>'
# 添加一个萌萌的看板娘
if ADD_WAIFU:
js += """
<script src="file=docs/waifu_plugin/jquery.min.js"></script>
<script src="file=docs/waifu_plugin/jquery-ui.min.js"></script>
<script src="file=docs/waifu_plugin/autoload.js"></script>
"""
gradio_original_template_fn = gr.routes.templates.TemplateResponse
def gradio_new_template_fn(*args, **kwargs):
res = gradio_original_template_fn(*args, **kwargs)
res.body = res.body.replace(b'</html>', f'{js}</html>'.encode("utf8"))
res.init_headers()
return res
gr.routes.templates.TemplateResponse = gradio_new_template_fn # override gradio template
except:
set_theme = None
print('gradio版本较旧, 不能自定义字体和颜色')
return set_theme
with open("docs/assets/custom.css", "r", encoding="utf-8") as f:
customCSS = f.read()
custom_css = customCSS
advanced_css = """
#debug_mes {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
z-index: 1; /* 设置更高的 z-index 值 */
margin-bottom: 10px !important;
}
#chat_txt {
display: flex;
flex-direction: column-reverse;
overflow-y: auto !important;
z-index: 3;
flex-grow: 1; /* 自动填充剩余空间 */
position: absolute;
bottom: 0;
left: 0;
width: 100%;
margin-bottom: 35px !important;
}
#sm_btn {
display: flex;
flex-wrap: unset !important;
gap: 5px !important;
width: var(--size-full);
}
textarea {
resize: none;
height: 100%; /* 填充父元素的高度 */
}
#main_chatbot {
height: 75vh !important;
max-height: 75vh !important;
/* overflow: auto !important; */
z-index: 2;
}
#prompt_result{
height: 60vh !important;
max-height: 60vh !important;
}
.wrap.svelte-18telvq.svelte-18telvq {
padding: var(--block-padding) !important;
height: 100% !important;
max-height: 95% !important;
overflow-y: auto !important;
}
.app.svelte-1mya07g.svelte-1mya07g {
max-width: 100%;
position: relative;
/* margin: auto; */
padding: var(--size-4);
width: 100%;
height: 100%;
}
.md-message table {
.markdown-body table {
margin: 1em 0;
border-collapse: collapse;
empty-cells: show;
}
.md-message th, .md-message td {
.markdown-body th, .markdown-body td {
border: 1.2px solid var(--border-color-primary);
padding: 5px;
}
.md-message thead {
.markdown-body thead {
background-color: rgba(175,184,193,0.2);
}
.md-message thead th {
.markdown-body thead th {
padding: .5em .2em;
}
.md-message ol, .md-message ul {
.markdown-body ol, .markdown-body ul {
padding-inline-start: 2em !important;
}
/* chat box. */
[class *= "message"] {
gap: 7px !important;
border-radius: var(--radius-xl) !important;
/* padding: var(--spacing-xl) !important; */
/* font-size: var(--text-md) !important; */
@ -217,40 +32,27 @@ textarea {
}
[data-testid = "bot"] {
max-width: 95%;
letter-spacing: 0.5px;
font-weight: normal;
/* width: auto !important; */
border-bottom-left-radius: 0 !important;
}
.dark [data-testid = "bot"] {
max-width: 95%;
color: #ccd2db !important;
letter-spacing: 0.5px;
font-weight: normal;
/* width: auto !important; */
border-bottom-left-radius: 0 !important;
}
[data-testid = "user"] {
max-width: 100%;
letter-spacing: 0.5px;
/* width: auto !important; */
border-bottom-right-radius: 0 !important;
}
/* linein code block. */
.md-message code {
.markdown-body code {
display: inline;
white-space: break-spaces;
border-radius: 6px;
margin: 0 2px 0 2px;
padding: .2em .4em .1em .4em;
background-color: rgba(13, 17, 23, 0.95);
color: #eff0f2;
color: #c9d1d9;
}
.dark .md-message code {
.dark .markdown-body code {
display: inline;
white-space: break-spaces;
border-radius: 6px;
@ -260,7 +62,7 @@ textarea {
}
/* code block css */
.md-message pre code {
.markdown-body pre code {
display: block;
overflow: auto;
white-space: pre;
@ -270,7 +72,7 @@ textarea {
margin: 1em 2em 1em 0.5em;
}
.dark .md-message pre code {
.dark .markdown-body pre code {
display: block;
overflow: auto;
white-space: pre;
@ -280,10 +82,15 @@ textarea {
margin: 1em 2em 1em 0.5em;
}
"""
/* .mic-wrap.svelte-1thnwz {
if CODE_HIGHLIGHT:
advanced_css += """
} */
.block.svelte-mppz8v > .mic-wrap.svelte-1thnwz{
justify-content: center;
display: flex;
padding: 0;
}
.codehilite .hll { background-color: #6e7681 }
.codehilite .c { color: #8b949e; font-style: italic } /* Comment */
@ -443,4 +250,3 @@ if CODE_HIGHLIGHT:
.dark .codehilite .vm { color: #82AAFF } /* Name.Variable.Magic */
.dark .codehilite .il { color: #F78C6C } /* Literal.Number.Integer.Long */
"""

87
theme/default.py Normal file
View File

@ -0,0 +1,87 @@
import gradio as gr
from toolbox import get_conf
CODE_HIGHLIGHT, ADD_WAIFU, LAYOUT = get_conf('CODE_HIGHLIGHT', 'ADD_WAIFU', 'LAYOUT')
def adjust_theme():
try:
color_er = gr.themes.utils.colors.fuchsia
set_theme = gr.themes.Default(
primary_hue=gr.themes.utils.colors.orange,
neutral_hue=gr.themes.utils.colors.gray,
font=["sans-serif", "Microsoft YaHei", "ui-sans-serif", "system-ui",
"sans-serif", gr.themes.utils.fonts.GoogleFont("Source Sans Pro")],
font_mono=["ui-monospace", "Consolas", "monospace", gr.themes.utils.fonts.GoogleFont("IBM Plex Mono")])
set_theme.set(
# Colors
input_background_fill_dark="*neutral_800",
# Transition
button_transition="none",
# Shadows
button_shadow="*shadow_drop",
button_shadow_hover="*shadow_drop_lg",
button_shadow_active="*shadow_inset",
input_shadow="0 0 0 *shadow_spread transparent, *shadow_inset",
input_shadow_focus="0 0 0 *shadow_spread *secondary_50, *shadow_inset",
input_shadow_focus_dark="0 0 0 *shadow_spread *neutral_700, *shadow_inset",
checkbox_label_shadow="*shadow_drop",
block_shadow="*shadow_drop",
form_gap_width="1px",
# Button borders
input_border_width="1px",
input_background_fill="white",
# Gradients
stat_background_fill="linear-gradient(to right, *primary_400, *primary_200)",
stat_background_fill_dark="linear-gradient(to right, *primary_400, *primary_600)",
error_background_fill=f"linear-gradient(to right, {color_er.c100}, *background_fill_secondary)",
error_background_fill_dark="*background_fill_primary",
checkbox_label_background_fill="linear-gradient(to top, *neutral_50, white)",
checkbox_label_background_fill_dark="linear-gradient(to top, *neutral_900, *neutral_800)",
checkbox_label_background_fill_hover="linear-gradient(to top, *neutral_100, white)",
checkbox_label_background_fill_hover_dark="linear-gradient(to top, *neutral_900, *neutral_800)",
button_primary_background_fill="linear-gradient(to bottom right, *primary_100, *primary_300)",
button_primary_background_fill_dark="linear-gradient(to bottom right, *primary_500, *primary_600)",
button_primary_background_fill_hover="linear-gradient(to bottom right, *primary_100, *primary_200)",
button_primary_background_fill_hover_dark="linear-gradient(to bottom right, *primary_500, *primary_500)",
button_primary_border_color_dark="*primary_500",
button_secondary_background_fill="linear-gradient(to bottom right, *neutral_100, *neutral_200)",
button_secondary_background_fill_dark="linear-gradient(to bottom right, *neutral_600, *neutral_700)",
button_secondary_background_fill_hover="linear-gradient(to bottom right, *neutral_100, *neutral_100)",
button_secondary_background_fill_hover_dark="linear-gradient(to bottom right, *neutral_600, *neutral_600)",
button_cancel_background_fill=f"linear-gradient(to bottom right, {color_er.c100}, {color_er.c200})",
button_cancel_background_fill_dark=f"linear-gradient(to bottom right, {color_er.c600}, {color_er.c700})",
button_cancel_background_fill_hover=f"linear-gradient(to bottom right, {color_er.c100}, {color_er.c100})",
button_cancel_background_fill_hover_dark=f"linear-gradient(to bottom right, {color_er.c600}, {color_er.c600})",
button_cancel_border_color=color_er.c200,
button_cancel_border_color_dark=color_er.c600,
button_cancel_text_color=color_er.c600,
button_cancel_text_color_dark="white",
)
if LAYOUT=="TOP-DOWN":
js = ""
else:
with open('theme/common.js', 'r', encoding='utf8') as f:
js = f"<script>{f.read()}</script>"
# 添加一个萌萌的看板娘
if ADD_WAIFU:
js += """
<script src="file=docs/waifu_plugin/jquery.min.js"></script>
<script src="file=docs/waifu_plugin/jquery-ui.min.js"></script>
<script src="file=docs/waifu_plugin/autoload.js"></script>
"""
gradio_original_template_fn = gr.routes.templates.TemplateResponse
def gradio_new_template_fn(*args, **kwargs):
res = gradio_original_template_fn(*args, **kwargs)
res.body = res.body.replace(b'</html>', f'{js}</html>'.encode("utf8"))
res.init_headers()
return res
gr.routes.templates.TemplateResponse = gradio_new_template_fn # override gradio template
except:
set_theme = None
print('gradio版本较旧, 不能自定义字体和颜色')
return set_theme
with open("theme/default.css", "r", encoding="utf-8") as f:
advanced_css = f.read()

View File

@ -167,6 +167,7 @@ footer {
font-size: 85%;
opacity: 0.60;
}
/* user_info */
#float_display {
position: absolute;
@ -241,16 +242,15 @@ textarea.svelte-1pie7s6 {
transition: height 0.3s ease;
}
.wrap.svelte-18telvq.svelte-18telvq {
/* .wrap.svelte-18telvq.svelte-18telvq {
padding: var(--block-padding) !important;
height: 100% !important;
max-height: 95% !important;
overflow-y: auto !important;
}
}*/
.app.svelte-1mya07g.svelte-1mya07g {
max-width: 100%;
position: relative;
/* margin: auto; */
padding: var(--size-4);
width: 100%;
height: 100%;

104
theme/green.py Normal file
View File

@ -0,0 +1,104 @@
import gradio as gr
from toolbox import get_conf
CODE_HIGHLIGHT, ADD_WAIFU, LAYOUT = get_conf('CODE_HIGHLIGHT', 'ADD_WAIFU', 'LAYOUT')
def adjust_theme():
try:
set_theme = gr.themes.Soft(
primary_hue=gr.themes.Color(
c50="#EBFAF2",
c100="#CFF3E1",
c200="#A8EAC8",
c300="#77DEA9",
c400="#3FD086",
c500="#02C160",
c600="#06AE56",
c700="#05974E",
c800="#057F45",
c900="#04673D",
c950="#2E5541",
name="small_and_beautiful",
),
secondary_hue=gr.themes.Color(
c50="#576b95",
c100="#576b95",
c200="#576b95",
c300="#576b95",
c400="#576b95",
c500="#576b95",
c600="#576b95",
c700="#576b95",
c800="#576b95",
c900="#576b95",
c950="#576b95",
),
neutral_hue=gr.themes.Color(
name="gray",
c50="#f6f7f8",
# c100="#f3f4f6",
c100="#F2F2F2",
c200="#e5e7eb",
c300="#d1d5db",
c400="#B2B2B2",
c500="#808080",
c600="#636363",
c700="#515151",
c800="#393939",
# c900="#272727",
c900="#2B2B2B",
c950="#171717",
),
radius_size=gr.themes.sizes.radius_sm,
).set(
button_primary_background_fill="*primary_500",
button_primary_background_fill_dark="*primary_600",
button_primary_background_fill_hover="*primary_400",
button_primary_border_color="*primary_500",
button_primary_border_color_dark="*primary_600",
button_primary_text_color="wihte",
button_primary_text_color_dark="white",
button_secondary_background_fill="*neutral_100",
button_secondary_background_fill_hover="*neutral_50",
button_secondary_background_fill_dark="*neutral_900",
button_secondary_text_color="*neutral_800",
button_secondary_text_color_dark="white",
background_fill_primary="#F7F7F7",
background_fill_primary_dark="#1F1F1F",
block_title_text_color="*primary_500",
block_title_background_fill_dark="*primary_900",
block_label_background_fill_dark="*primary_900",
input_background_fill="#F6F6F6",
chatbot_code_background_color="*neutral_950",
chatbot_code_background_color_dark="*neutral_950",
)
js = ''
if LAYOUT=="TOP-DOWN":
js = ""
else:
with open('theme/common.js', 'r', encoding='utf8') as f:
js = f"<script>{f.read()}</script>"
# 添加一个萌萌的看板娘
if ADD_WAIFU:
js += """
<script src="file=docs/waifu_plugin/jquery.min.js"></script>
<script src="file=docs/waifu_plugin/jquery-ui.min.js"></script>
<script src="file=docs/waifu_plugin/autoload.js"></script>
"""
gradio_original_template_fn = gr.routes.templates.TemplateResponse
def gradio_new_template_fn(*args, **kwargs):
res = gradio_original_template_fn(*args, **kwargs)
res.body = res.body.replace(b'</html>', f'{js}</html>'.encode("utf8"))
res.init_headers()
return res
gr.routes.templates.TemplateResponse = gradio_new_template_fn # override gradio template
except:
set_theme = None
print('gradio版本较旧, 不能自定义字体和颜色')
return set_theme
with open("theme/green.css", "r", encoding="utf-8") as f:
advanced_css = f.read()

12
theme/theme.py Normal file
View File

@ -0,0 +1,12 @@
import gradio as gr
from toolbox import get_conf
THEME, = get_conf('THEME')
if THEME == 'Chuanhu-Small-and-Beautiful':
from .green import adjust_theme, advanced_css
theme_declaration = "<h2 align=\"center\" class=\"small\">[Chuanhu-Small-and-Beautiful主题]</h2>"
else:
from .default import adjust_theme, advanced_css
theme_declaration = ""

View File

@ -1,18 +1,12 @@
import html
import markdown
import importlib
import time
import inspect
import gradio as gr
import func_box
import re
import os
import gradio
from latex2mathml.converter import convert as tex2mathml
from functools import wraps, lru_cache
import shutil
import os
import time
import glob
import sys
import threading
############################### 插件输入输出接驳区 #######################################
pj = os.path.join
"""
@ -47,62 +41,57 @@ def ArgsGeneralWrapper(f):
"""
装饰器函数,用于重组输入参数,改变输入参数的顺序与结构。
"""
def decorated(cookies, max_length, llm_model, txt, top_p, temperature,
chatbot, history, system_prompt, models, plugin_advanced_arg, ipaddr: gr.Request, *args):
""""""
def decorated(request: gradio.Request, cookies, max_length, llm_model, txt, txt2, top_p, temperature, chatbot, history, system_prompt, plugin_advanced_arg, *args):
txt_passon = txt
if txt == "" and txt2 != "": txt_passon = txt2
# 引入一个有cookie的chatbot
start_time = time.time()
encrypt, private = get_conf('switch_model')[0]['key']
private_key, = get_conf('private_key')
cookies.update({
'top_p':top_p,
'temperature':temperature,
})
llm_kwargs = {
'api_key': cookies['api_key'],
'llm_model': llm_model,
'top_p':top_p,
'max_length': max_length,
'temperature': temperature,
'ipaddr': ipaddr.client.host,
'start_time': start_time
'temperature':temperature,
'client_ip': request.client.host,
}
plugin_kwargs = {
"advanced_arg": plugin_advanced_arg,
"parameters_def": ''
}
if len(args) > 1:
plugin_kwargs.update({'parameters_def': args[1]})
transparent_address_private = f'<p style="display:none;">\n{private_key}\n{ipaddr.client.host}\n</p>'
transparent_address = f'<p style="display:none;">\n{ipaddr.client.host}\n</p>'
if private in models:
if chatbot == []:
chatbot.append([None, f'隐私模式, 你的对话记录无法被他人检索 {transparent_address_private}'])
else:
chatbot[0] = [None, f'隐私模式, 你的对话记录无法被他人检索 {transparent_address_private}']
else:
if chatbot == []:
chatbot.append([None, f'正常对话模式, 你接来下的对话将会被记录并且可以被所有人检索你可以到Settings中选择隐私模式 {transparent_address}'])
else:
chatbot[0] = [None, f'正常对话模式, 你接来下的对话将会被记录并且可以被所有人检索你可以到Settings中选择隐私模式 {transparent_address}']
chatbot_with_cookie = ChatBotWithCookies(cookies)
chatbot_with_cookie.write_list(chatbot)
txt_passon = txt
if encrypt in models: txt_passon = func_box.encryption_str(txt)
yield from f(txt_passon, llm_kwargs, plugin_kwargs, chatbot_with_cookie, history, system_prompt, *args)
if cookies.get('lock_plugin', None) is None:
# 正常状态
yield from f(txt_passon, llm_kwargs, plugin_kwargs, chatbot_with_cookie, history, system_prompt, *args)
else:
# 处理个别特殊插件的锁定状态
module, fn_name = cookies['lock_plugin'].split('->')
f_hot_reload = getattr(importlib.import_module(module, fn_name), fn_name)
yield from f_hot_reload(txt_passon, llm_kwargs, plugin_kwargs, chatbot_with_cookie, history, system_prompt, *args)
return decorated
def update_ui(chatbot, history, msg='正常', *args): # 刷新界面
def update_ui(chatbot, history, msg='正常', **kwargs): # 刷新界面
"""
刷新用户界面
"""
assert isinstance(chatbot, ChatBotWithCookies), "在传递chatbot的过程中不要将其丢弃。必要时可用clear将其清空然后用for+append循环重新赋值。"
yield chatbot.get_cookies(), chatbot, history, msg
threading.Thread(target=func_box.thread_write_chat, args=(chatbot, history)).start()
# func_box.thread_write_chat(chatbot, history)
assert isinstance(chatbot, ChatBotWithCookies), "在传递chatbot的过程中不要将其丢弃。必要时, 可用clear将其清空, 然后用for+append循环重新赋值。"
cookies = chatbot.get_cookies()
# 解决插件锁定时的界面显示问题
if cookies.get('lock_plugin', None):
label = cookies.get('llm_model', "") + " | " + "正在锁定插件" + cookies.get('lock_plugin', None)
chatbot_gr = gradio.update(value=chatbot, label=label)
if cookies.get('label', "") != label: cookies['label'] = label # 记住当前的label
elif cookies.get('label', None):
chatbot_gr = gradio.update(value=chatbot, label=cookies.get('llm_model', ""))
cookies['label'] = None # 清空label
else:
chatbot_gr = chatbot
yield cookies, chatbot_gr, history, msg
def update_ui_lastest_msg(lastmsg, chatbot, history, delay=1): # 刷新界面
"""
@ -159,14 +148,9 @@ def HotReload(f):
def decorated(*args, **kwargs):
fn_name = f.__name__
f_hot_reload = getattr(importlib.reload(inspect.getmodule(f)), fn_name)
try:
yield from f_hot_reload(*args, **kwargs)
except TypeError:
args = tuple(args[element] for element in range(len(args)) if element != 6)
yield from f_hot_reload(*args, **kwargs)
yield from f_hot_reload(*args, **kwargs)
return decorated
####################################### 其他小工具 #####################################
"""
========================================================================
@ -230,7 +214,8 @@ def write_results_to_file(history, file_name=None):
# remove everything that cannot be handled by utf8
f.write(content.encode('utf-8', 'ignore').decode())
f.write('\n\n')
res = '以上材料已经被写入' + f'./gpt_log/{file_name}'
res = '以上材料已经被写入:\t' + os.path.abspath(f'./gpt_log/{file_name}')
print(res)
return res
@ -255,51 +240,37 @@ def report_execption(chatbot, history, a, b):
history.append(b)
import re
def text_divide_paragraph(input_str):
if input_str:
code_blocks = re.findall(r'```[\s\S]*?```', input_str)
def text_divide_paragraph(text):
"""
将文本按照段落分隔符分割开生成带有段落标签的HTML代码。
"""
pre = '<div class="markdown-body">'
suf = '</div>'
if text.startswith(pre) and text.endswith(suf):
return text
if '```' in text:
# careful input
return pre + text + suf
else:
# wtf input
lines = text.split("\n")
for i, line in enumerate(lines):
lines[i] = lines[i].replace(" ", "&nbsp;")
text = "</br>".join(lines)
return pre + text + suf
for i, block in enumerate(code_blocks):
input_str = input_str.replace(block, f'{{{{CODE_BLOCK_{i}}}}}')
if code_blocks:
sections = re.split(r'({{{{\w+}}}})', input_str)
for idx, section in enumerate(sections):
if 'CODE_BLOCK' in section or section.startswith(' '):
continue
sections[idx] = re.sub(r'(?!```)(?<!\n)\n(?!(\n|^)( {0,3}[\*\+\-]|[0-9]+\.))', '\n\n', section)
input_str = ''.join(sections)
for i, block in enumerate(code_blocks):
input_str = input_str.replace(f'{{{{CODE_BLOCK_{i}}}}}', block.replace('\n', '\n'))
else:
lines = input_str.split('\n')
for idx, line in enumerate(lines[:-1]):
if not line.strip():
continue
if not (lines[idx + 1].startswith(' ') or lines[idx + 1].startswith('\t')):
lines[idx] += '\n' # 将一个换行符替换为两个换行符
input_str = '\n'.join(lines)
return input_str
@lru_cache(maxsize=128) # 使用 lru缓存 加快转换速度
@lru_cache(maxsize=128) # 使用 lru缓存 加快转换速度
def markdown_convertion(txt):
"""
将Markdown格式的文本转换为HTML格式。如果包含数学公式则先将公式转换为HTML格式。
"""
pre = '<div class="md-message">'
pre = '<div class="markdown-body">'
suf = '</div>'
raw_pre = '<div class="raw-message hideM">'
raw_suf = '</div>'
if txt.startswith(pre) and txt.endswith(suf):
# print('警告,输入了已经经过转化的字符串,二次转化可能出问题')
return txt # 已经被转化过,不需要再次转化
if txt.startswith(raw_pre) and txt.endswith(raw_suf):
return txt # 已经被转化过,不需要再次转化
raw_hide = raw_pre + txt + raw_suf
return txt # 已经被转化过,不需要再次转化
markdown_extension_configs = {
'mdx_math': {
'enable_dollar_delimiter': True,
@ -308,6 +279,13 @@ def markdown_convertion(txt):
}
find_equation_pattern = r'<script type="math/tex(?:.*?)>(.*?)</script>'
def tex2mathml_catch_exception(content, *args, **kwargs):
try:
content = tex2mathml(content, *args, **kwargs)
except:
content = content
return content
def replace_math_no_render(match):
content = match.group(1)
if 'mode=display' in match.group(0):
@ -323,47 +301,40 @@ def markdown_convertion(txt):
content = content.replace('\\begin{aligned}', '\\begin{array}')
content = content.replace('\\end{aligned}', '\\end{array}')
content = content.replace('&', ' ')
content = tex2mathml(content, display="block")
content = tex2mathml_catch_exception(content, display="block")
return content
else:
return tex2mathml(content)
return tex2mathml_catch_exception(content)
def markdown_bug_hunt(content):
"""
解决一个mdx_math的bug单$包裹begin命令时多余<script>
"""
content = content.replace('<script type="math/tex">\n<script type="math/tex; mode=display">',
'<script type="math/tex; mode=display">')
content = content.replace('<script type="math/tex">\n<script type="math/tex; mode=display">', '<script type="math/tex; mode=display">')
content = content.replace('</script>\n</script>', '</script>')
return content
def no_code(txt):
if '```' not in txt:
if '```' not in txt:
return True
else:
if '```reference' in txt:
return True # newbing
else:
return False
if '```reference' in txt: return True # newbing
else: return False
if ('$$' in txt) and no_code(txt): # 有$标识的公式符号,且没有代码段```的标识
if ('$' in txt) and no_code(txt): # 有$标识的公式符号,且没有代码段```的标识
# convert everything to html format
split = markdown.markdown(text='---')
txt = re.sub(r'\$\$((?:.|\n)*?)\$\$', lambda match: '$$' + re.sub(r'\n+', '</br>', match.group(1)) + '$$', txt)
convert_stage_1 = markdown.markdown(text=txt, extensions=['mdx_math', 'fenced_code', 'tables', 'sane_lists'], extension_configs=markdown_extension_configs)
convert_stage_1 = markdown_bug_hunt(convert_stage_1)
# re.DOTALL: Make the '.' special character match any character at all, including a newline; without this flag, '.' will match anything except a newline. Corresponds to the inline flag (?s).
# 1. convert to easy-to-copy tex (do not render math)
convert_stage_2_1, n = re.subn(find_equation_pattern, replace_math_no_render, convert_stage_1, flags=re.DOTALL)
# 2. convert to rendered equation
convert_stage_1_resp = convert_stage_1.replace('</br>', '')
convert_stage_2_2, n = re.subn(find_equation_pattern, replace_math_render, convert_stage_1_resp, flags=re.DOTALL)
convert_stage_2_2, n = re.subn(find_equation_pattern, replace_math_render, convert_stage_1, flags=re.DOTALL)
# cat them together
context = pre + convert_stage_2_1 + f'{split}' + convert_stage_2_2 + suf
return raw_hide + context # 破坏html 结构,并显示源码
return pre + convert_stage_2_1 + f'{split}' + convert_stage_2_2 + suf
else:
context = pre + markdown.markdown(txt, extensions=['fenced_code', 'codehilite', 'tables', 'sane_lists']) + suf
return raw_hide + context # 破坏html 结构,并显示源码
return pre + markdown.markdown(txt, extensions=['fenced_code', 'codehilite', 'tables', 'sane_lists']) + suf
def close_up_code_segment_during_stream(gpt_reply):
@ -377,9 +348,9 @@ def close_up_code_segment_during_stream(gpt_reply):
str: 返回一个新的字符串,将输出代码片段的“后面的```”补上。
"""
if '```' not in str(gpt_reply):
if '```' not in gpt_reply:
return gpt_reply
if str(gpt_reply).endswith('```'):
if gpt_reply.endswith('```'):
return gpt_reply
# 排除了以上两个情况,我们
@ -405,8 +376,7 @@ def format_io(self, y):
if gpt_reply is not None: gpt_reply = close_up_code_segment_during_stream(gpt_reply)
# process
y[-1] = (
# None if i_ask is None else markdown.markdown(i_ask, extensions=['fenced_code', 'tables']),
None if i_ask is None else markdown_convertion(i_ask),
None if i_ask is None else markdown.markdown(i_ask, extensions=['fenced_code', 'tables']),
None if gpt_reply is None else markdown_convertion(gpt_reply)
)
return y
@ -497,58 +467,52 @@ def promote_file_to_downloadzone(file, rename_file=None, chatbot=None):
import shutil
if rename_file is None: rename_file = f'{gen_time_str()}-{os.path.basename(file)}'
new_path = os.path.join(f'./gpt_log/', rename_file)
# 如果已经存在,先删除
if os.path.exists(new_path) and not os.path.samefile(new_path, file): os.remove(new_path)
# 把文件复制过去
if not os.path.exists(new_path): shutil.copyfile(file, new_path)
# 将文件添加到chatbot cookie中避免多用户干扰
if chatbot:
if 'file_to_promote' in chatbot._cookies: current = chatbot._cookies['file_to_promote']
else: current = []
chatbot._cookies.update({'file_to_promote': [new_path] + current})
def get_user_upload(chatbot, ipaddr: gr.Request):
"""
获取用户上传过的文件
"""
private_upload = './private_upload'
user_history = os.path.join(private_upload, ipaddr.client.host)
history = """| 编号 | 目录 | 目录内文件 |\n| --- | --- | --- |\n"""
count_num = 1
for root, d, file in os.walk(user_history):
file_link = "<br>".join([f'{func_box.html_view_blank(f"{root}/{i}")}' for i in file])
history += f'| {count_num} | {root} | {file_link} |\n'
count_num += 1
chatbot.append(['Load Submission History....',
f'[Local Message] 请自行复制以下目录 or 目录+文件, 填入输入框以供函数区高亮按钮使用\n\n'
f'{func_box.html_tag_color("提交前记得请检查头尾空格哦~")}\n\n'
f'{history}'
])
return chatbot
def on_file_uploaded(files, chatbot, txt, ipaddr: gr.Request):
def on_file_uploaded(files, chatbot, txt, txt2, checkboxes):
"""
当文件被上传时的回调函数
"""
if len(files) == 0:
return chatbot, txt
private_upload = './private_upload'
# shutil.rmtree('./private_upload/') 不需要删除文件
time_tag_path = os.path.join(private_upload, ipaddr.client.host, time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()))
os.makedirs(f'{time_tag_path}', exist_ok=True)
import shutil
import os
import time
import glob
from toolbox import extract_archive
try:
shutil.rmtree('./private_upload/')
except:
pass
time_tag = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
os.makedirs(f'private_upload/{time_tag}', exist_ok=True)
err_msg = ''
for file in files:
file_origin_name = os.path.basename(file.orig_name)
shutil.copy(file.name, f'{time_tag_path}/{file_origin_name}')
err_msg += extract_archive(f'{time_tag_path}/{file_origin_name}',
dest_dir=f'{time_tag_path}/{file_origin_name}.extract')
moved_files = [fp for fp in glob.glob(f'{time_tag_path}/**/*', recursive=True)]
txt = f'{time_tag_path}'
shutil.copy(file.name, f'private_upload/{time_tag}/{file_origin_name}')
err_msg += extract_archive(f'private_upload/{time_tag}/{file_origin_name}',
dest_dir=f'private_upload/{time_tag}/{file_origin_name}.extract')
moved_files = [fp for fp in glob.glob('private_upload/**/*', recursive=True)]
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([None,
chatbot.append(['我上传了文件,请查收',
f'[Local Message] 收到以下文件: \n\n{moved_files_str}' +
f'\n\n调用路径参数已自动修正到: \n\n{txt}' +
f'\n\n现在您点击任意“高亮”标识的函数插件时,以上文件将被作为输入参数'+err_msg])
return chatbot, txt
f'\n\n现在您点击任意“红颜色”标识的函数插件时,以上文件将被作为输入参数'+err_msg])
return chatbot, txt, txt2
def on_report_generated(cookies, files, chatbot):
@ -566,23 +530,24 @@ def on_report_generated(cookies, files, chatbot):
chatbot.append(['报告如何远程获取?', f'报告已经添加到右侧“文件上传区”(可能处于折叠状态),请查收。{file_links}'])
return cookies, report_files, chatbot
def load_chat_cookies():
API_KEY, LLM_MODEL, AZURE_API_KEY = get_conf('API_KEY', 'LLM_MODEL', 'AZURE_API_KEY')
if is_any_api_key(AZURE_API_KEY):
if is_any_api_key(API_KEY): API_KEY = API_KEY + ',' + AZURE_API_KEY
else: API_KEY = AZURE_API_KEY
return {'api_key': API_KEY, 'llm_model': LLM_MODEL}
def is_openai_api_key(key):
API_MATCH_ORIGINAL = re.match(r"sk-[a-zA-Z0-9]{48}$", key)
return bool(API_MATCH_ORIGINAL)
def is_azure_api_key(key):
API_MATCH_AZURE = re.match(r"[a-zA-Z0-9]{32}$", key)
return bool(API_MATCH_ORIGINAL) or bool(API_MATCH_AZURE)
return bool(API_MATCH_AZURE)
def is_api2d_key(key):
if key.startswith('fk') and len(key) == 41:
return True
else:
return False
def is_proxy_key(key):
if key.startswith('proxy-') and len(key) == 38:
return True
else:
return False
API_MATCH_API2D = re.match(r"fk[a-zA-Z0-9]{6}-[a-zA-Z0-9]{32}$", key)
return bool(API_MATCH_API2D)
def is_any_api_key(key):
if ',' in key:
@ -591,10 +556,10 @@ def is_any_api_key(key):
if is_any_api_key(k): return True
return False
else:
return is_openai_api_key(key) or is_api2d_key(key) or is_proxy_key(key)
return is_openai_api_key(key) or is_api2d_key(key) or is_azure_api_key(key)
def what_keys(keys):
avail_key_list = {'OpenAI Key':0, "API2D Key":0}
avail_key_list = {'OpenAI Key':0, "Azure Key":0, "API2D Key":0}
key_list = keys.split(',')
for k in key_list:
@ -606,13 +571,10 @@ def what_keys(keys):
avail_key_list['API2D Key'] += 1
for k in key_list:
if is_proxy_key(k):
avail_key_list['Proxy Key'] += 1
if is_azure_api_key(k):
avail_key_list['Azure Key'] += 1
return f"检测到: \n" \
f"OpenAI Key {avail_key_list['OpenAI Key']}\n" \
f"API2D Key {avail_key_list['API2D Key']}\n" \
f"Proxy Key {avail_key_list['API2D Key']}\n"
return f"检测到: OpenAI Key {avail_key_list['OpenAI Key']} 个, Azure Key {avail_key_list['Azure Key']} 个, API2D Key {avail_key_list['API2D Key']}"
def select_api_key(keys, llm_model):
import random
@ -627,12 +589,12 @@ def select_api_key(keys, llm_model):
for k in key_list:
if is_api2d_key(k): avail_key_list.append(k)
if llm_model.startswith('proxy-'):
if llm_model.startswith('azure-'):
for k in key_list:
if is_proxy_key(k): avail_key_list.append(k.replace('proxy-', ''))
if is_azure_api_key(k): avail_key_list.append(k)
if len(avail_key_list) == 0:
raise RuntimeError(f"您提供的api-key不满足要求不包含任何可用于{llm_model}的api-key。您可能选择了错误的模型或请求源")
raise RuntimeError(f"您提供的api-key不满足要求不包含任何可用于{llm_model}的api-key。您可能选择了错误的模型或请求源右下角更换模型菜单中可切换openai,azure和api2d请求源")
api_key = random.choice(avail_key_list) # 随机负载均衡
return api_key
@ -701,12 +663,6 @@ def read_single_conf_with_lru_cache(arg):
except:
try:
# 优先级2. 获取config_private中的配置
# 获取当前文件所在目录的路径
current_dir = os.path.dirname(os.path.abspath(__file__))
# 获取上一层目录的路径
parent_dir = os.path.dirname(current_dir)
# 将上一层目录添加到Python的搜索路径中
sys.path.append(parent_dir)
r = getattr(importlib.import_module('config_private'), arg)
except:
# 优先级3. 获取config中的配置
@ -761,7 +717,6 @@ class DummyWith():
def __exit__(self, exc_type, exc_value, traceback):
return
def run_gradio_in_subpath(demo, auth, port, custom_path):
"""
把gradio的运行地址更改到指定的二次路径上
@ -928,3 +883,4 @@ def objload(file='objdump.tmp'):
return
with open(file, 'rb') as f:
return pickle.load(f)

View File

@ -1,5 +1,5 @@
{
"version": 3.42,
"version": 3.45,
"show_feature": true,
"new_feature": "完善本地Latex矫错和翻译功能 <-> 增加gpt-3.5-16k的支持 <-> 新增最强Arxiv论文翻译插件 <-> 修复gradio复制按钮BUG <-> 修复PDF翻译的BUG, 新增HTML中英双栏对照 <-> 添加了OpenAI图片生成插件 <-> 添加了OpenAI音频转文本总结插件 <-> 通过Slack添加对Claude的支持"
"new_feature": "支持加载自定义的ChatGLM2微调模型 <-> [改善UI] 动态ChatBot窗口高度 <-> 修复Azure接口的BUG <-> 完善多语言模块 <-> 完善本地Latex矫错和翻译功能 <-> 增加gpt-3.5-16k的支持 <-> 新增最强Arxiv论文翻译插件 <-> 修复gradio复制按钮BUG <-> 修复PDF翻译的BUG, 新增HTML中英双栏对照 <-> 添加了OpenAI图片生成插件"
}