JohnSmith9982
commited on
Commit
•
4dfaeff
1
Parent(s):
632a25e
Upload 114 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- CITATION.cff +5 -5
- ChuanhuChatbot.py +152 -66
- Dockerfile +11 -8
- config_example.json +55 -14
- locale/en_US.json +23 -9
- locale/ja_JP.json +23 -9
- locale/ko_KR.json +89 -0
- locale/sv-SE.json +87 -0
- modules/.DS_Store +0 -0
- modules/__pycache__/config.cpython-311.pyc +0 -0
- modules/__pycache__/config.cpython-39.pyc +0 -0
- modules/__pycache__/index_func.cpython-311.pyc +0 -0
- modules/__pycache__/index_func.cpython-39.pyc +0 -0
- modules/__pycache__/overwrites.cpython-311.pyc +0 -0
- modules/__pycache__/overwrites.cpython-39.pyc +0 -0
- modules/__pycache__/pdf_func.cpython-311.pyc +0 -0
- modules/__pycache__/presets.cpython-311.pyc +0 -0
- modules/__pycache__/presets.cpython-39.pyc +0 -0
- modules/__pycache__/repo.cpython-311.pyc +0 -0
- modules/__pycache__/shared.cpython-311.pyc +0 -0
- modules/__pycache__/shared.cpython-39.pyc +0 -0
- modules/__pycache__/train_func.cpython-311.pyc +0 -0
- modules/__pycache__/utils.cpython-311.pyc +0 -0
- modules/__pycache__/utils.cpython-39.pyc +0 -0
- modules/__pycache__/webui.cpython-311.pyc +0 -0
- modules/config.py +123 -33
- modules/index_func.py +15 -20
- modules/models/Google_PaLM.py +26 -0
- modules/models/__pycache__/ChuanhuAgent.cpython-311.pyc +0 -0
- modules/models/__pycache__/Google_PaLM.cpython-311.pyc +0 -0
- modules/models/__pycache__/azure.cpython-311.pyc +0 -0
- modules/models/__pycache__/base_model.cpython-311.pyc +0 -0
- modules/models/__pycache__/base_model.cpython-39.pyc +0 -0
- modules/models/__pycache__/models.cpython-311.pyc +0 -0
- modules/models/__pycache__/models.cpython-39.pyc +0 -0
- modules/models/azure.py +17 -0
- modules/models/base_model.py +130 -28
- modules/models/midjourney.py +385 -0
- modules/models/models.py +34 -17
- modules/overwrites.py +33 -20
- modules/presets.py +10 -2
- modules/repo.py +239 -0
- modules/shared.py +1 -0
- modules/train_func.py +161 -0
- modules/utils.py +85 -58
- modules/webui.py +70 -0
- readme/README_en.md +21 -8
- readme/README_ja.md +21 -8
- requirements.txt +8 -5
- requirements_advanced.txt +11 -0
CITATION.cff
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
cff-version: 1.2.0
|
2 |
-
title:
|
3 |
message: >-
|
4 |
If you use this software, please cite it using these
|
5 |
metadata.
|
@@ -13,8 +13,8 @@ authors:
|
|
13 |
orcid: https://orcid.org/0009-0005-0357-272X
|
14 |
repository-code: 'https://github.com/GaiZhenbiao/ChuanhuChatGPT'
|
15 |
url: 'https://github.com/GaiZhenbiao/ChuanhuChatGPT'
|
16 |
-
abstract:
|
17 |
license: GPL-3.0
|
18 |
-
commit:
|
19 |
-
version: '
|
20 |
-
date-released: '2023-
|
|
|
1 |
cff-version: 1.2.0
|
2 |
+
title: Chuanhu Chat
|
3 |
message: >-
|
4 |
If you use this software, please cite it using these
|
5 |
metadata.
|
|
|
13 |
orcid: https://orcid.org/0009-0005-0357-272X
|
14 |
repository-code: 'https://github.com/GaiZhenbiao/ChuanhuChatGPT'
|
15 |
url: 'https://github.com/GaiZhenbiao/ChuanhuChatGPT'
|
16 |
+
abstract: This software provides a light and easy-to-use interface for ChatGPT API and many LLMs.
|
17 |
license: GPL-3.0
|
18 |
+
commit: c6c08bc62ef80e37c8be52f65f9b6051a7eea1fa
|
19 |
+
version: '20230709'
|
20 |
+
date-released: '2023-07-09'
|
ChuanhuChatbot.py
CHANGED
@@ -1,8 +1,11 @@
|
|
1 |
# -*- coding:utf-8 -*-
|
2 |
-
import os
|
3 |
import logging
|
4 |
-
|
|
|
|
|
|
|
5 |
|
|
|
6 |
import gradio as gr
|
7 |
|
8 |
from modules import config
|
@@ -10,6 +13,9 @@ from modules.config import *
|
|
10 |
from modules.utils import *
|
11 |
from modules.presets import *
|
12 |
from modules.overwrites import *
|
|
|
|
|
|
|
13 |
from modules.models.models import get_model
|
14 |
|
15 |
logging.getLogger("httpx").setLevel(logging.WARNING)
|
@@ -17,13 +23,13 @@ logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
17 |
gr.Chatbot._postprocess_chat_messages = postprocess_chat_messages
|
18 |
gr.Chatbot.postprocess = postprocess
|
19 |
|
20 |
-
with open("
|
21 |
-
|
22 |
|
23 |
def create_new_model():
|
24 |
return get_model(model_name = MODELS[DEFAULT_MODEL], access_key = my_api_key)[0]
|
25 |
|
26 |
-
with gr.Blocks(
|
27 |
user_name = gr.State("")
|
28 |
promptTemplates = gr.State(load_template(get_template_names(plain=True)[0], mode=2))
|
29 |
user_question = gr.State("")
|
@@ -34,31 +40,45 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
34 |
topic = gr.State(i18n("未命名对话历史记录"))
|
35 |
|
36 |
with gr.Row():
|
37 |
-
gr.HTML(CHUANHU_TITLE, elem_id="
|
38 |
-
status_display = gr.Markdown(get_geoip(), elem_id="
|
39 |
-
with gr.Row(elem_id="
|
40 |
-
user_info = gr.Markdown(value="getting user info...", elem_id="
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
with gr.Column(scale=5):
|
44 |
with gr.Row():
|
45 |
-
chatbot = gr.Chatbot(label="Chuanhu Chat", elem_id="
|
46 |
with gr.Row():
|
47 |
with gr.Column(min_width=225, scale=12):
|
48 |
user_input = gr.Textbox(
|
49 |
-
elem_id="
|
50 |
-
show_label=False, placeholder=i18n("在这里输入")
|
51 |
-
|
|
|
52 |
with gr.Column(min_width=42, scale=1):
|
53 |
-
submitBtn = gr.Button(value="", variant="primary", elem_id="
|
54 |
-
cancelBtn = gr.Button(value="", variant="secondary", visible=False, elem_id="
|
55 |
-
with gr.Row():
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
|
|
|
|
|
|
|
|
62 |
with gr.Row(visible=False) as like_dislike_area:
|
63 |
with gr.Column(min_width=20, scale=1):
|
64 |
likeBtn = gr.Button(i18n("👍"))
|
@@ -77,9 +97,9 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
77 |
label="API-Key",
|
78 |
)
|
79 |
if multi_api_key:
|
80 |
-
usageTxt = gr.Markdown(i18n("多账号模式已开启,无需输入key,可直接开始对话"), elem_id="
|
81 |
else:
|
82 |
-
usageTxt = gr.Markdown(i18n("**发送消息** 或 **提交key** 以显示额度"), elem_id="
|
83 |
model_select_dropdown = gr.Dropdown(
|
84 |
label=i18n("选择模型"), choices=MODELS, multiselect=False, value=MODELS[DEFAULT_MODEL], interactive=True
|
85 |
)
|
@@ -87,15 +107,15 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
87 |
label=i18n("选择LoRA模型"), choices=[], multiselect=False, interactive=True, visible=False
|
88 |
)
|
89 |
with gr.Row():
|
90 |
-
single_turn_checkbox = gr.Checkbox(label=i18n("单轮对话"), value=False)
|
91 |
-
use_websearch_checkbox = gr.Checkbox(label=i18n("使用在线搜索"), value=False)
|
92 |
language_select_dropdown = gr.Dropdown(
|
93 |
label=i18n("选择回复语言(针对搜索&索引功能)"),
|
94 |
choices=REPLY_LANGUAGES,
|
95 |
multiselect=False,
|
96 |
value=REPLY_LANGUAGES[0],
|
97 |
)
|
98 |
-
index_files = gr.Files(label=i18n("上传"), type="file")
|
99 |
two_column = gr.Checkbox(label=i18n("双栏pdf"), value=advance_docs["pdf"].get("two_column", False))
|
100 |
summarize_btn = gr.Button(i18n("总结"))
|
101 |
# TODO: 公式ocr
|
@@ -107,8 +127,8 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
107 |
placeholder=i18n("在这里输入System Prompt..."),
|
108 |
label="System prompt",
|
109 |
value=INITIAL_SYSTEM_PROMPT,
|
110 |
-
lines=10
|
111 |
-
)
|
112 |
with gr.Accordion(label=i18n("加载Prompt模板"), open=True):
|
113 |
with gr.Column():
|
114 |
with gr.Row():
|
@@ -118,7 +138,8 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
118 |
choices=get_template_names(plain=True),
|
119 |
multiselect=False,
|
120 |
value=get_template_names(plain=True)[0],
|
121 |
-
|
|
|
122 |
with gr.Column(scale=1):
|
123 |
templateRefreshBtn = gr.Button(i18n("🔄 刷新"))
|
124 |
with gr.Row():
|
@@ -129,7 +150,8 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
129 |
get_template_names(plain=True)[0], mode=1
|
130 |
),
|
131 |
multiselect=False,
|
132 |
-
|
|
|
133 |
|
134 |
with gr.Tab(label=i18n("保存/加载")):
|
135 |
with gr.Accordion(label=i18n("保存/加载对话历史记录"), open=True):
|
@@ -139,10 +161,14 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
139 |
historyFileSelectDropdown = gr.Dropdown(
|
140 |
label=i18n("从列表中加载对话"),
|
141 |
choices=get_history_names(plain=True),
|
142 |
-
multiselect=False
|
|
|
143 |
)
|
144 |
-
with gr.
|
145 |
-
|
|
|
|
|
|
|
146 |
with gr.Row():
|
147 |
with gr.Column(scale=6):
|
148 |
saveFileName = gr.Textbox(
|
@@ -150,7 +176,9 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
150 |
placeholder=i18n("设置文件名: 默认为.json,可选为.md"),
|
151 |
label=i18n("设置保存文件名"),
|
152 |
value=i18n("对话历史记录"),
|
153 |
-
|
|
|
|
|
154 |
with gr.Column(scale=1):
|
155 |
saveHistoryBtn = gr.Button(i18n("💾 保存对话"))
|
156 |
exportMarkdownBtn = gr.Button(i18n("📝 导出为Markdown"))
|
@@ -159,12 +187,32 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
159 |
with gr.Column():
|
160 |
downloadFile = gr.File(interactive=True)
|
161 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
162 |
with gr.Tab(label=i18n("高级")):
|
163 |
-
gr.
|
164 |
-
gr.HTML(get_html("appearance_switcher.html").format(label=i18n("切换亮暗色主题")), elem_classes="insert_block")
|
165 |
use_streaming_checkbox = gr.Checkbox(
|
166 |
-
label=i18n("实时传输回答"), value=True, visible=ENABLE_STREAMING_OPTION
|
167 |
)
|
|
|
|
|
168 |
with gr.Accordion(i18n("参数"), open=False):
|
169 |
temperature_slider = gr.Slider(
|
170 |
minimum=-0,
|
@@ -192,7 +240,7 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
192 |
)
|
193 |
stop_sequence_txt = gr.Textbox(
|
194 |
show_label=True,
|
195 |
-
placeholder=i18n("
|
196 |
label="stop",
|
197 |
value="",
|
198 |
lines=1,
|
@@ -244,25 +292,36 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
244 |
lines=1,
|
245 |
)
|
246 |
|
247 |
-
with gr.Accordion(i18n("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
248 |
# 优先展示自定义的api_host
|
249 |
apihostTxt = gr.Textbox(
|
250 |
show_label=True,
|
251 |
-
placeholder=
|
252 |
-
label="API-Host",
|
253 |
value=config.api_host or shared.API_HOST,
|
254 |
lines=1,
|
|
|
|
|
|
|
255 |
)
|
256 |
-
changeAPIURLBtn = gr.Button(i18n("🔄 切换API地址"))
|
257 |
-
|
258 |
-
|
259 |
-
placeholder=i18n("在这里输入代理地址..."),
|
260 |
-
label=i18n("代理地址(示例:http://127.0.0.1:10809)"),
|
261 |
-
value="",
|
262 |
-
lines=2,
|
263 |
-
)
|
264 |
-
changeProxyBtn = gr.Button(i18n("🔄 设置代理地址"))
|
265 |
-
default_btn = gr.Button(i18n("🔙 恢复默认设置"))
|
266 |
|
267 |
gr.Markdown(CHUANHU_DESCRIPTION, elem_id="description")
|
268 |
gr.HTML(get_html("footer.html").format(versions=versions_html()), elem_id="footer")
|
@@ -323,6 +382,10 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
323 |
outputs=[saveFileName, systemPromptTxt, chatbot]
|
324 |
)
|
325 |
|
|
|
|
|
|
|
|
|
326 |
|
327 |
# Chatbot
|
328 |
cancelBtn.click(interrupt, [current_model], [])
|
@@ -341,6 +404,7 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
341 |
inputs=[current_model],
|
342 |
outputs=[chatbot, status_display],
|
343 |
show_progress=True,
|
|
|
344 |
)
|
345 |
|
346 |
retryBtn.click(**start_outputing_args).then(
|
@@ -391,7 +455,7 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
391 |
keyTxt.change(set_key, [current_model, keyTxt], [user_api_key, status_display], api_name="set_key").then(**get_usage_args)
|
392 |
keyTxt.submit(**get_usage_args)
|
393 |
single_turn_checkbox.change(set_single_turn, [current_model, single_turn_checkbox], None)
|
394 |
-
model_select_dropdown.change(get_model, [model_select_dropdown, lora_select_dropdown, user_api_key, temperature_slider, top_p_slider, systemPromptTxt, user_name], [current_model, status_display, chatbot, lora_select_dropdown], show_progress=True, api_name="get_model")
|
395 |
model_select_dropdown.change(toggle_like_btn_visibility, [model_select_dropdown], [like_dislike_area], show_progress=False)
|
396 |
lora_select_dropdown.change(get_model, [model_select_dropdown, lora_select_dropdown, user_api_key, temperature_slider, top_p_slider, systemPromptTxt, user_name], [current_model, status_display, chatbot], show_progress=True)
|
397 |
|
@@ -425,10 +489,23 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
425 |
downloadFile,
|
426 |
show_progress=True,
|
427 |
)
|
428 |
-
historyRefreshBtn.click(
|
|
|
429 |
historyFileSelectDropdown.change(**load_history_from_file_args)
|
430 |
downloadFile.change(upload_chat_history, [current_model, downloadFile, user_name], [saveFileName, systemPromptTxt, chatbot])
|
431 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
432 |
# Advanced
|
433 |
max_context_length_slider.change(set_token_upper_limit, [current_model, max_context_length_slider], None)
|
434 |
temperature_slider.change(set_temperature, [current_model, temperature_slider], None)
|
@@ -444,15 +521,24 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
444 |
default_btn.click(
|
445 |
reset_default, [], [apihostTxt, proxyTxt, status_display], show_progress=True
|
446 |
)
|
447 |
-
changeAPIURLBtn.click(
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
)
|
453 |
-
changeProxyBtn.click(
|
454 |
-
|
455 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
456 |
[status_display],
|
457 |
show_progress=True,
|
458 |
)
|
@@ -469,5 +555,5 @@ if __name__ == "__main__":
|
|
469 |
reload_javascript()
|
470 |
demo.queue(concurrency_count=CONCURRENT_COUNT).launch(
|
471 |
blocked_paths=["config.json"],
|
472 |
-
favicon_path="./
|
473 |
)
|
|
|
1 |
# -*- coding:utf-8 -*-
|
|
|
2 |
import logging
|
3 |
+
logging.basicConfig(
|
4 |
+
level=logging.INFO,
|
5 |
+
format="%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s",
|
6 |
+
)
|
7 |
|
8 |
+
import colorama
|
9 |
import gradio as gr
|
10 |
|
11 |
from modules import config
|
|
|
13 |
from modules.utils import *
|
14 |
from modules.presets import *
|
15 |
from modules.overwrites import *
|
16 |
+
from modules.webui import *
|
17 |
+
from modules.repo import *
|
18 |
+
from modules.train_func import *
|
19 |
from modules.models.models import get_model
|
20 |
|
21 |
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
|
23 |
gr.Chatbot._postprocess_chat_messages = postprocess_chat_messages
|
24 |
gr.Chatbot.postprocess = postprocess
|
25 |
|
26 |
+
# with open("web_assets/css/ChuanhuChat.css", "r", encoding="utf-8") as f:
|
27 |
+
# ChuanhuChatCSS = f.read()
|
28 |
|
29 |
def create_new_model():
|
30 |
return get_model(model_name = MODELS[DEFAULT_MODEL], access_key = my_api_key)[0]
|
31 |
|
32 |
+
with gr.Blocks(theme=small_and_beautiful_theme) as demo:
|
33 |
user_name = gr.State("")
|
34 |
promptTemplates = gr.State(load_template(get_template_names(plain=True)[0], mode=2))
|
35 |
user_question = gr.State("")
|
|
|
40 |
topic = gr.State(i18n("未命名对话历史记录"))
|
41 |
|
42 |
with gr.Row():
|
43 |
+
gr.HTML(CHUANHU_TITLE, elem_id="app-title")
|
44 |
+
status_display = gr.Markdown(get_geoip(), elem_id="status-display")
|
45 |
+
with gr.Row(elem_id="float-display"):
|
46 |
+
user_info = gr.Markdown(value="getting user info...", elem_id="user-info")
|
47 |
+
config_info = gr.HTML(get_html("config_info.html").format(bot_avatar=config.bot_avatar, user_avatar=config.user_avatar), visible=False, elem_id="config-info")
|
48 |
+
update_info = gr.HTML(get_html("update.html").format(
|
49 |
+
current_version=repo_tag_html(),
|
50 |
+
version_time=version_time(),
|
51 |
+
cancel_btn=i18n("取消"),
|
52 |
+
update_btn=i18n("更新"),
|
53 |
+
seenew_btn=i18n("详情"),
|
54 |
+
ok_btn=i18n("好"),
|
55 |
+
), visible=check_update)
|
56 |
+
|
57 |
+
with gr.Row(equal_height=True):
|
58 |
with gr.Column(scale=5):
|
59 |
with gr.Row():
|
60 |
+
chatbot = gr.Chatbot(label="Chuanhu Chat", elem_id="chuanhu-chatbot", latex_delimiters=latex_delimiters_set, height=700)
|
61 |
with gr.Row():
|
62 |
with gr.Column(min_width=225, scale=12):
|
63 |
user_input = gr.Textbox(
|
64 |
+
elem_id="user-input-tb",
|
65 |
+
show_label=False, placeholder=i18n("在这里输入"),
|
66 |
+
container=False
|
67 |
+
)
|
68 |
with gr.Column(min_width=42, scale=1):
|
69 |
+
submitBtn = gr.Button(value="", variant="primary", elem_id="submit-btn")
|
70 |
+
cancelBtn = gr.Button(value="", variant="secondary", visible=False, elem_id="cancel-btn")
|
71 |
+
with gr.Row(elem_id="chatbot-buttons"):
|
72 |
+
with gr.Column(min_width=120, scale=1):
|
73 |
+
emptyBtn = gr.Button(
|
74 |
+
i18n("🧹 新的对话"), elem_id="empty-btn"
|
75 |
+
)
|
76 |
+
with gr.Column(min_width=120, scale=1):
|
77 |
+
retryBtn = gr.Button(i18n("🔄 重新生成"))
|
78 |
+
with gr.Column(min_width=120, scale=1):
|
79 |
+
delFirstBtn = gr.Button(i18n("🗑️ 删除最旧对话"))
|
80 |
+
with gr.Column(min_width=120, scale=1):
|
81 |
+
delLastBtn = gr.Button(i18n("🗑️ 删除最新对话"))
|
82 |
with gr.Row(visible=False) as like_dislike_area:
|
83 |
with gr.Column(min_width=20, scale=1):
|
84 |
likeBtn = gr.Button(i18n("👍"))
|
|
|
97 |
label="API-Key",
|
98 |
)
|
99 |
if multi_api_key:
|
100 |
+
usageTxt = gr.Markdown(i18n("多账号模式已开启,无需输入key,可直接开始对话"), elem_id="usage-display", elem_classes="insert-block", visible=show_api_billing)
|
101 |
else:
|
102 |
+
usageTxt = gr.Markdown(i18n("**发送消息** 或 **提交key** 以显示额度"), elem_id="usage-display", elem_classes="insert-block", visible=show_api_billing)
|
103 |
model_select_dropdown = gr.Dropdown(
|
104 |
label=i18n("选择模型"), choices=MODELS, multiselect=False, value=MODELS[DEFAULT_MODEL], interactive=True
|
105 |
)
|
|
|
107 |
label=i18n("选择LoRA模型"), choices=[], multiselect=False, interactive=True, visible=False
|
108 |
)
|
109 |
with gr.Row():
|
110 |
+
single_turn_checkbox = gr.Checkbox(label=i18n("单轮对话"), value=False, elem_classes="switch-checkbox")
|
111 |
+
use_websearch_checkbox = gr.Checkbox(label=i18n("使用在线搜索"), value=False, elem_classes="switch-checkbox")
|
112 |
language_select_dropdown = gr.Dropdown(
|
113 |
label=i18n("选择回复语言(针对搜索&索引功能)"),
|
114 |
choices=REPLY_LANGUAGES,
|
115 |
multiselect=False,
|
116 |
value=REPLY_LANGUAGES[0],
|
117 |
)
|
118 |
+
index_files = gr.Files(label=i18n("上传"), type="file", elem_id="upload-index-file")
|
119 |
two_column = gr.Checkbox(label=i18n("双栏pdf"), value=advance_docs["pdf"].get("two_column", False))
|
120 |
summarize_btn = gr.Button(i18n("总结"))
|
121 |
# TODO: 公式ocr
|
|
|
127 |
placeholder=i18n("在这里输入System Prompt..."),
|
128 |
label="System prompt",
|
129 |
value=INITIAL_SYSTEM_PROMPT,
|
130 |
+
lines=10
|
131 |
+
)
|
132 |
with gr.Accordion(label=i18n("加载Prompt模板"), open=True):
|
133 |
with gr.Column():
|
134 |
with gr.Row():
|
|
|
138 |
choices=get_template_names(plain=True),
|
139 |
multiselect=False,
|
140 |
value=get_template_names(plain=True)[0],
|
141 |
+
container=False,
|
142 |
+
)
|
143 |
with gr.Column(scale=1):
|
144 |
templateRefreshBtn = gr.Button(i18n("🔄 刷新"))
|
145 |
with gr.Row():
|
|
|
150 |
get_template_names(plain=True)[0], mode=1
|
151 |
),
|
152 |
multiselect=False,
|
153 |
+
container=False,
|
154 |
+
)
|
155 |
|
156 |
with gr.Tab(label=i18n("保存/加载")):
|
157 |
with gr.Accordion(label=i18n("保存/加载对话历史记录"), open=True):
|
|
|
161 |
historyFileSelectDropdown = gr.Dropdown(
|
162 |
label=i18n("从列表中加载对话"),
|
163 |
choices=get_history_names(plain=True),
|
164 |
+
multiselect=False,
|
165 |
+
container=False,
|
166 |
)
|
167 |
+
with gr.Row():
|
168 |
+
with gr.Column(min_width=42, scale=1):
|
169 |
+
historyRefreshBtn = gr.Button(i18n("🔄 刷新"))
|
170 |
+
with gr.Column(min_width=42, scale=1):
|
171 |
+
historyDeleteBtn = gr.Button(i18n("🗑️ 删除"))
|
172 |
with gr.Row():
|
173 |
with gr.Column(scale=6):
|
174 |
saveFileName = gr.Textbox(
|
|
|
176 |
placeholder=i18n("设置文件名: 默认为.json,可选为.md"),
|
177 |
label=i18n("设置保存文件名"),
|
178 |
value=i18n("对话历史记录"),
|
179 |
+
elem_classes="no-container"
|
180 |
+
# container=False,
|
181 |
+
)
|
182 |
with gr.Column(scale=1):
|
183 |
saveHistoryBtn = gr.Button(i18n("💾 保存对话"))
|
184 |
exportMarkdownBtn = gr.Button(i18n("📝 导出为Markdown"))
|
|
|
187 |
with gr.Column():
|
188 |
downloadFile = gr.File(interactive=True)
|
189 |
|
190 |
+
with gr.Tab(label=i18n("微调")):
|
191 |
+
openai_train_status = gr.Markdown(label=i18n("训练状态"), value=i18n("在这里[查看使用介绍](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B#%E5%BE%AE%E8%B0%83-gpt-35)"))
|
192 |
+
|
193 |
+
with gr.Tab(label=i18n("准备数据集")):
|
194 |
+
dataset_preview_json = gr.JSON(label=i18n("数据集预览"), readonly=True)
|
195 |
+
dataset_selection = gr.Files(label = i18n("选择数据集"), file_types=[".xlsx", ".jsonl"], file_count="single")
|
196 |
+
upload_to_openai_btn = gr.Button(i18n("上传到OpenAI"), variant="primary", interactive=False)
|
197 |
+
|
198 |
+
with gr.Tab(label=i18n("训练")):
|
199 |
+
openai_ft_file_id = gr.Textbox(label=i18n("文件ID"), value="", lines=1, placeholder=i18n("上传到 OpenAI 后自动填充"))
|
200 |
+
openai_ft_suffix = gr.Textbox(label=i18n("模型名称后缀"), value="", lines=1, placeholder=i18n("可选,用于区分不同的模型"))
|
201 |
+
openai_train_epoch_slider = gr.Slider(label=i18n("训练轮数(Epochs)"), minimum=1, maximum=100, value=3, step=1, interactive=True)
|
202 |
+
openai_start_train_btn = gr.Button(i18n("开始训练"), variant="primary", interactive=False)
|
203 |
+
|
204 |
+
with gr.Tab(label=i18n("状态")):
|
205 |
+
openai_status_refresh_btn = gr.Button(i18n("刷新状态"))
|
206 |
+
openai_cancel_all_jobs_btn = gr.Button(i18n("取消所有任务"))
|
207 |
+
add_to_models_btn = gr.Button(i18n("添加训练好的模型到模型列表"), interactive=False)
|
208 |
+
|
209 |
with gr.Tab(label=i18n("高级")):
|
210 |
+
gr.HTML(get_html("appearance_switcher.html").format(label=i18n("切换亮暗色主题")), elem_classes="insert-block")
|
|
|
211 |
use_streaming_checkbox = gr.Checkbox(
|
212 |
+
label=i18n("实时传输回答"), value=True, visible=ENABLE_STREAMING_OPTION, elem_classes="switch-checkbox"
|
213 |
)
|
214 |
+
checkUpdateBtn = gr.Button(i18n("🔄 检查更新..."), visible=check_update)
|
215 |
+
gr.Markdown(i18n("# ⚠️ 务必谨慎更改 ⚠️"), elem_id="advanced-warning")
|
216 |
with gr.Accordion(i18n("参数"), open=False):
|
217 |
temperature_slider = gr.Slider(
|
218 |
minimum=-0,
|
|
|
240 |
)
|
241 |
stop_sequence_txt = gr.Textbox(
|
242 |
show_label=True,
|
243 |
+
placeholder=i18n("停止符,用英文逗号隔开..."),
|
244 |
label="stop",
|
245 |
value="",
|
246 |
lines=1,
|
|
|
292 |
lines=1,
|
293 |
)
|
294 |
|
295 |
+
with gr.Accordion(i18n("网络参数"), open=False):
|
296 |
+
gr.Markdown(i18n("---\n⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置"), elem_id="netsetting-warning")
|
297 |
+
default_btn = gr.Button(i18n("🔙 恢复默认网络设置"))
|
298 |
+
# 网络代理
|
299 |
+
proxyTxt = gr.Textbox(
|
300 |
+
show_label=True,
|
301 |
+
placeholder=i18n("未设置代理..."),
|
302 |
+
label=i18n("代理地址"),
|
303 |
+
value=config.http_proxy,
|
304 |
+
lines=1,
|
305 |
+
interactive=False,
|
306 |
+
# container=False,
|
307 |
+
elem_classes="view-only-textbox no-container",
|
308 |
+
)
|
309 |
+
# changeProxyBtn = gr.Button(i18n("🔄 设置代理地址"))
|
310 |
+
|
311 |
# 优先展示自定义的api_host
|
312 |
apihostTxt = gr.Textbox(
|
313 |
show_label=True,
|
314 |
+
placeholder="api.openai.com",
|
315 |
+
label="OpenAI API-Host",
|
316 |
value=config.api_host or shared.API_HOST,
|
317 |
lines=1,
|
318 |
+
interactive=False,
|
319 |
+
# container=False,
|
320 |
+
elem_classes="view-only-textbox no-container",
|
321 |
)
|
322 |
+
# changeAPIURLBtn = gr.Button(i18n("🔄 切换API地址"))
|
323 |
+
updateChuanhuBtn = gr.Button(visible=False, elem_classes="invisible-btn", elem_id="update-chuanhu-btn")
|
324 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
325 |
|
326 |
gr.Markdown(CHUANHU_DESCRIPTION, elem_id="description")
|
327 |
gr.HTML(get_html("footer.html").format(versions=versions_html()), elem_id="footer")
|
|
|
382 |
outputs=[saveFileName, systemPromptTxt, chatbot]
|
383 |
)
|
384 |
|
385 |
+
refresh_history_args = dict(
|
386 |
+
fn=get_history_names, inputs=[gr.State(False), user_name], outputs=[historyFileSelectDropdown]
|
387 |
+
)
|
388 |
+
|
389 |
|
390 |
# Chatbot
|
391 |
cancelBtn.click(interrupt, [current_model], [])
|
|
|
404 |
inputs=[current_model],
|
405 |
outputs=[chatbot, status_display],
|
406 |
show_progress=True,
|
407 |
+
_js='clearChatbot',
|
408 |
)
|
409 |
|
410 |
retryBtn.click(**start_outputing_args).then(
|
|
|
455 |
keyTxt.change(set_key, [current_model, keyTxt], [user_api_key, status_display], api_name="set_key").then(**get_usage_args)
|
456 |
keyTxt.submit(**get_usage_args)
|
457 |
single_turn_checkbox.change(set_single_turn, [current_model, single_turn_checkbox], None)
|
458 |
+
model_select_dropdown.change(get_model, [model_select_dropdown, lora_select_dropdown, user_api_key, temperature_slider, top_p_slider, systemPromptTxt, user_name], [current_model, status_display, chatbot, lora_select_dropdown, user_api_key, keyTxt], show_progress=True, api_name="get_model")
|
459 |
model_select_dropdown.change(toggle_like_btn_visibility, [model_select_dropdown], [like_dislike_area], show_progress=False)
|
460 |
lora_select_dropdown.change(get_model, [model_select_dropdown, lora_select_dropdown, user_api_key, temperature_slider, top_p_slider, systemPromptTxt, user_name], [current_model, status_display, chatbot], show_progress=True)
|
461 |
|
|
|
489 |
downloadFile,
|
490 |
show_progress=True,
|
491 |
)
|
492 |
+
historyRefreshBtn.click(**refresh_history_args)
|
493 |
+
historyDeleteBtn.click(delete_chat_history, [current_model, historyFileSelectDropdown, user_name], [status_display, historyFileSelectDropdown, chatbot], _js='(a,b,c)=>{return showConfirmationDialog(a, b, c);}')
|
494 |
historyFileSelectDropdown.change(**load_history_from_file_args)
|
495 |
downloadFile.change(upload_chat_history, [current_model, downloadFile, user_name], [saveFileName, systemPromptTxt, chatbot])
|
496 |
|
497 |
+
# Train
|
498 |
+
dataset_selection.upload(handle_dataset_selection, dataset_selection, [dataset_preview_json, upload_to_openai_btn, openai_train_status])
|
499 |
+
dataset_selection.clear(handle_dataset_clear, [], [dataset_preview_json, upload_to_openai_btn])
|
500 |
+
upload_to_openai_btn.click(upload_to_openai, [dataset_selection], [openai_ft_file_id, openai_train_status], show_progress=True)
|
501 |
+
|
502 |
+
openai_ft_file_id.change(lambda x: gr.update(interactive=True) if len(x) > 0 else gr.update(interactive=False), [openai_ft_file_id], [openai_start_train_btn])
|
503 |
+
openai_start_train_btn.click(start_training, [openai_ft_file_id, openai_ft_suffix, openai_train_epoch_slider], [openai_train_status])
|
504 |
+
|
505 |
+
openai_status_refresh_btn.click(get_training_status, [], [openai_train_status, add_to_models_btn])
|
506 |
+
add_to_models_btn.click(add_to_models, [], [model_select_dropdown, openai_train_status], show_progress=True)
|
507 |
+
openai_cancel_all_jobs_btn.click(cancel_all_jobs, [], [openai_train_status], show_progress=True)
|
508 |
+
|
509 |
# Advanced
|
510 |
max_context_length_slider.change(set_token_upper_limit, [current_model, max_context_length_slider], None)
|
511 |
temperature_slider.change(set_temperature, [current_model, temperature_slider], None)
|
|
|
521 |
default_btn.click(
|
522 |
reset_default, [], [apihostTxt, proxyTxt, status_display], show_progress=True
|
523 |
)
|
524 |
+
# changeAPIURLBtn.click(
|
525 |
+
# change_api_host,
|
526 |
+
# [apihostTxt],
|
527 |
+
# [status_display],
|
528 |
+
# show_progress=True,
|
529 |
+
# )
|
530 |
+
# changeProxyBtn.click(
|
531 |
+
# change_proxy,
|
532 |
+
# [proxyTxt],
|
533 |
+
# [status_display],
|
534 |
+
# show_progress=True,
|
535 |
+
# )
|
536 |
+
checkUpdateBtn.click(fn=None, _js='manualCheckUpdate')
|
537 |
+
|
538 |
+
# Invisible elements
|
539 |
+
updateChuanhuBtn.click(
|
540 |
+
update_chuanhu,
|
541 |
+
[],
|
542 |
[status_display],
|
543 |
show_progress=True,
|
544 |
)
|
|
|
555 |
reload_javascript()
|
556 |
demo.queue(concurrency_count=CONCURRENT_COUNT).launch(
|
557 |
blocked_paths=["config.json"],
|
558 |
+
favicon_path="./web_assets/favicon.ico",
|
559 |
)
|
Dockerfile
CHANGED
@@ -1,15 +1,18 @@
|
|
1 |
-
FROM python:3.9 as builder
|
2 |
-
RUN apt-get update
|
|
|
|
|
|
|
3 |
COPY requirements.txt .
|
4 |
COPY requirements_advanced.txt .
|
5 |
-
RUN pip install --user -r requirements.txt
|
6 |
-
# RUN pip install --user -r requirements_advanced.txt
|
7 |
|
8 |
-
FROM python:3.9
|
9 |
-
|
10 |
COPY --from=builder /root/.local /root/.local
|
11 |
ENV PATH=/root/.local/bin:$PATH
|
12 |
COPY . /app
|
13 |
WORKDIR /app
|
14 |
-
ENV dockerrun
|
15 |
-
CMD ["python3", "-u", "ChuanhuChatbot.py",
|
|
|
1 |
+
FROM python:3.9-slim-buster as builder
|
2 |
+
RUN apt-get update \
|
3 |
+
&& apt-get install -y build-essential \
|
4 |
+
&& apt-get clean \
|
5 |
+
&& rm -rf /var/lib/apt/lists/*
|
6 |
COPY requirements.txt .
|
7 |
COPY requirements_advanced.txt .
|
8 |
+
RUN pip install --user --no-cache-dir -r requirements.txt
|
9 |
+
# RUN pip install --user --no-cache-dir -r requirements_advanced.txt
|
10 |
|
11 |
+
FROM python:3.9-slim-buster
|
12 |
+
LABEL maintainer="iskoldt"
|
13 |
COPY --from=builder /root/.local /root/.local
|
14 |
ENV PATH=/root/.local/bin:$PATH
|
15 |
COPY . /app
|
16 |
WORKDIR /app
|
17 |
+
ENV dockerrun=yes
|
18 |
+
CMD ["python3", "-u", "ChuanhuChatbot.py","2>&1", "|", "tee", "/var/log/application.log"]
|
config_example.json
CHANGED
@@ -1,24 +1,60 @@
|
|
1 |
{
|
2 |
-
//
|
3 |
-
|
4 |
-
|
5 |
-
// 你的
|
6 |
-
"
|
7 |
-
"
|
8 |
-
//
|
9 |
-
|
10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
"users": [], // 用户列表,[[用户名1, 密码1], [用户名2, 密码2], ...]
|
12 |
"local_embedding": false, //是否在本地编制索引
|
|
|
|
|
13 |
"default_model": "gpt-3.5-turbo", // 默认模型
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
"advance_docs": {
|
15 |
"pdf": {
|
16 |
-
// 是否认为PDF是双栏的
|
17 |
-
"
|
18 |
-
// 是否使用OCR识别PDF中的公式
|
19 |
-
"formula_ocr": true
|
20 |
}
|
21 |
},
|
|
|
|
|
22 |
// 是否多个API Key轮换使用
|
23 |
"multi_api_key": false,
|
24 |
"api_key_list": [
|
@@ -26,7 +62,12 @@
|
|
26 |
"sk-xxxxxxxxxxxxxxxxxxxxxxxx2",
|
27 |
"sk-xxxxxxxxxxxxxxxxxxxxxxxx3"
|
28 |
],
|
29 |
-
//
|
|
|
|
|
|
|
|
|
|
|
30 |
// "server_name": "0.0.0.0",
|
31 |
// "server_port": 7860,
|
32 |
// 如果要share到gradio,设置为true
|
|
|
1 |
{
|
2 |
+
// 各配置具体说明,见 [https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#配置-configjson]
|
3 |
+
|
4 |
+
//== API 配置 ==
|
5 |
+
"openai_api_key": "", // 你的 OpenAI API Key,一般必填,若空缺则需在图形界面中填入API Key
|
6 |
+
"google_palm_api_key": "", // 你的 Google PaLM API Key,用于 Google PaLM 对话模型
|
7 |
+
"xmchat_api_key": "", // 你的 xmchat API Key,用于 XMChat 对话模型
|
8 |
+
"minimax_api_key": "", // 你的 MiniMax API Key,用于 MiniMax 对话模型
|
9 |
+
"minimax_group_id": "", // 你的 MiniMax Group ID,用于 MiniMax 对话模型
|
10 |
+
"midjourney_proxy_api_base": "https://xxx/mj", // 你的 https://github.com/novicezk/midjourney-proxy 代理地址
|
11 |
+
"midjourney_proxy_api_secret": "", // 你的 MidJourney Proxy API Secret,用于鉴权访问 api,可选
|
12 |
+
"midjourney_discord_proxy_url": "", // 你的 MidJourney Discord Proxy URL,用于对生成对图进行反代,可选
|
13 |
+
"midjourney_temp_folder": "./tmp", // 你的 MidJourney 临时文件夹,用于存放生成的图片,填空则关闭自动下载切图(直接显示MJ的四宫格图)
|
14 |
+
|
15 |
+
|
16 |
+
//== Azure ==
|
17 |
+
"openai_api_type": "openai", // 可选项:azure, openai
|
18 |
+
"azure_openai_api_key": "", // 你的 Azure OpenAI API Key,用于 Azure OpenAI 对话模型
|
19 |
+
"azure_openai_api_base_url": "", // 你的 Azure Base URL
|
20 |
+
"azure_openai_api_version": "2023-05-15", // 你的 Azure OpenAI API 版本
|
21 |
+
"azure_deployment_name": "", // 你的 Azure OpenAI Chat 模型 Deployment 名称
|
22 |
+
"azure_embedding_deployment_name": "", // 你的 Azure OpenAI Embedding 模型 Deployment 名称
|
23 |
+
"azure_embedding_model_name": "text-embedding-ada-002", // 你的 Azure OpenAI Embedding 模型名称
|
24 |
+
|
25 |
+
//== 基础配置 ==
|
26 |
+
"language": "auto", // 界面语言,可选"auto", "zh-CN", "en-US", "ja-JP", "ko-KR", "sv-SE"
|
27 |
"users": [], // 用户列表,[[用户名1, 密码1], [用户名2, 密码2], ...]
|
28 |
"local_embedding": false, //是否在本地编制索引
|
29 |
+
"hide_history_when_not_logged_in": false, //未登录情况下是否不展示对话历史
|
30 |
+
"check_update": true, //是否启用检查更新
|
31 |
"default_model": "gpt-3.5-turbo", // 默认模型
|
32 |
+
"bot_avatar": "default", // 机器人头像,可填写图片链接、Data URL (base64),或者"none"(不显示头像)
|
33 |
+
"user_avatar": "default", // 用户头像,可填写图片链接、Data URL (base64),或者"none"(不显示头像)
|
34 |
+
|
35 |
+
//== API 用量 ==
|
36 |
+
"show_api_billing": false, //是否显示OpenAI API用量(启用需要填写sensitive_id)
|
37 |
+
"sensitive_id": "", // 你 OpenAI 账户的 Sensitive ID,用于查询 API 用量
|
38 |
+
"usage_limit": 120, // 该 OpenAI API Key 的当月限额,单位:美元,用于计算百分比和显示上限
|
39 |
+
"legacy_api_usage": false, // 是否使用旧版 API 用量查询接口(OpenAI现已关闭该接口,但是如果你在使用第三方 API,第三方可能仍然支持此接口)
|
40 |
+
|
41 |
+
//== 川虎助理设置 ==
|
42 |
+
"default_chuanhu_assistant_model": "gpt-4", //川虎助理使用的模型,可选gpt-3.5-turbo或者gpt-4等
|
43 |
+
"GOOGLE_CSE_ID": "", //谷歌搜索引擎ID,用于川虎助理Pro模式,获取方式请看 https://stackoverflow.com/questions/37083058/programmatically-searching-google-in-python-using-custom-search
|
44 |
+
"GOOGLE_API_KEY": "", //谷歌API Key,用于川虎助理Pro模式
|
45 |
+
"WOLFRAM_ALPHA_APPID": "", //Wolfram Alpha API Key,用于川虎助理Pro模式,获取方式请看 https://products.wolframalpha.com/api/
|
46 |
+
"SERPAPI_API_KEY": "", //SerpAPI API Key,用于川虎助理Pro模式,获取方式请看 https://serpapi.com/
|
47 |
+
|
48 |
+
//== 文档处理与显示 ==
|
49 |
+
"latex_option": "default", // LaTeX 公式渲染策略,可选"default", "strict", "all"或者"disabled"
|
50 |
"advance_docs": {
|
51 |
"pdf": {
|
52 |
+
"two_column": false, // 是否认为PDF是双栏的
|
53 |
+
"formula_ocr": true // 是否使用OCR识别PDF中的公式
|
|
|
|
|
54 |
}
|
55 |
},
|
56 |
+
|
57 |
+
//== 高级配置 ==
|
58 |
// 是否多个API Key轮换使用
|
59 |
"multi_api_key": false,
|
60 |
"api_key_list": [
|
|
|
62 |
"sk-xxxxxxxxxxxxxxxxxxxxxxxx2",
|
63 |
"sk-xxxxxxxxxxxxxxxxxxxxxxxx3"
|
64 |
],
|
65 |
+
// 自定义OpenAI API Base
|
66 |
+
// "openai_api_base": "https://api.openai.com",
|
67 |
+
// 自定��使用代理(请替换代理URL)
|
68 |
+
// "https_proxy": "http://127.0.0.1:1079",
|
69 |
+
// "http_proxy": "http://127.0.0.1:1079",
|
70 |
+
// 自定义端口、自定义ip(请替换对应内容)
|
71 |
// "server_name": "0.0.0.0",
|
72 |
// "server_port": 7860,
|
73 |
// 如果要share到gradio,设置为true
|
locale/en_US.json
CHANGED
@@ -32,24 +32,33 @@
|
|
32 |
"📝 导出为Markdown": "📝 Export as Markdown",
|
33 |
"默认保存于history文件夹": "Default save in history folder",
|
34 |
"高级": "Advanced",
|
35 |
-
"# ⚠️ 务必谨慎更改
|
36 |
"参数": "Parameters",
|
37 |
-
"
|
38 |
"用于定位滥用行为": "Used to locate abuse",
|
39 |
"用户名": "Username",
|
40 |
-
"网络设置": "Network Settings",
|
41 |
"在这里输入API-Host...": "Type in API-Host here...",
|
42 |
"🔄 切换API地址": "🔄 Switch API Address",
|
43 |
-
"
|
44 |
-
"
|
45 |
"🔄 设置代理地址": "🔄 Set Proxy Address",
|
46 |
-
"🔙
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
"川虎Chat 🚀": "Chuanhu Chat 🚀",
|
48 |
"开始实时传输回答……": "Start streaming output...",
|
49 |
"Token 计数: ": "Token Count: ",
|
50 |
-
",本次对话累计消耗了 ": "
|
51 |
"**获取API使用情况失败**": "**Failed to get API usage**",
|
|
|
|
|
52 |
"**本月使用金额** ": "**Monthly usage** ",
|
|
|
53 |
"获取API使用情况失败:": "Failed to get API usage:",
|
54 |
"API密钥更改为了": "The API key is changed to",
|
55 |
"JSON解析错误,收到的内容: ": "JSON parsing error, received content: ",
|
@@ -64,10 +73,15 @@
|
|
64 |
"API key为空,请检查是否输入正确。": "API key is empty, check whether it is entered correctly.",
|
65 |
"请输入对话内容。": "Enter the content of the conversation.",
|
66 |
"账单信息不适用": "Billing information is not applicable",
|
67 |
-
"由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)
|
68 |
"切换亮暗色主题": "Switch light/dark theme",
|
69 |
"您的IP区域:未知。": "Your IP region: Unknown.",
|
70 |
"获取IP地理位置失败。原因:": "Failed to get IP location. Reason: ",
|
71 |
"。你仍然可以使用聊天功能。": ". You can still use the chat function.",
|
72 |
-
"您的IP区域:": "Your IP region: "
|
|
|
|
|
|
|
|
|
|
|
73 |
}
|
|
|
32 |
"📝 导出为Markdown": "📝 Export as Markdown",
|
33 |
"默认保存于history文件夹": "Default save in history folder",
|
34 |
"高级": "Advanced",
|
35 |
+
"# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ Caution: Changes require care. ⚠️",
|
36 |
"参数": "Parameters",
|
37 |
+
"停止符,用英文逗号隔开...": "Type in stop token here, separated by comma...",
|
38 |
"用于定位滥用行为": "Used to locate abuse",
|
39 |
"用户名": "Username",
|
|
|
40 |
"在这里输入API-Host...": "Type in API-Host here...",
|
41 |
"🔄 切换API地址": "🔄 Switch API Address",
|
42 |
+
"未设置代理...": "No proxy...",
|
43 |
+
"代理地址": "Proxy address",
|
44 |
"🔄 设置代理地址": "🔄 Set Proxy Address",
|
45 |
+
"🔙 恢复默认网络设置": "🔙 Reset Network Settings",
|
46 |
+
"🔄 检查更新...": "🔄 Check for Update...",
|
47 |
+
"取消": "Cancel",
|
48 |
+
"更新": "Update",
|
49 |
+
"详情": "Details",
|
50 |
+
"好": "OK",
|
51 |
+
"更新成功,请重启本程序": "Updated successfully, please restart this program",
|
52 |
+
"更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "Update failed, please try [manually updating](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)",
|
53 |
"川虎Chat 🚀": "Chuanhu Chat 🚀",
|
54 |
"开始实时传输回答……": "Start streaming output...",
|
55 |
"Token 计数: ": "Token Count: ",
|
56 |
+
",本次对话累计消耗了 ": ", Total cost for this dialogue is ",
|
57 |
"**获取API使用情况失败**": "**Failed to get API usage**",
|
58 |
+
"**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**Failed to get API usage**, correct sensitive_id needed in `config.json`",
|
59 |
+
"**获取API使用情况失败**,sensitive_id错误或已过期": "**Failed to get API usage**, wrong or expired sensitive_id",
|
60 |
"**本月使用金额** ": "**Monthly usage** ",
|
61 |
+
"本月使用金额": "Monthly usage",
|
62 |
"获取API使用情况失败:": "Failed to get API usage:",
|
63 |
"API密钥更改为了": "The API key is changed to",
|
64 |
"JSON解析错误,收到的内容: ": "JSON parsing error, received content: ",
|
|
|
73 |
"API key为空,请检查是否输入正确。": "API key is empty, check whether it is entered correctly.",
|
74 |
"请输入对话内容。": "Enter the content of the conversation.",
|
75 |
"账单信息不适用": "Billing information is not applicable",
|
76 |
+
"由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "Developed by Bilibili [土川虎虎虎](https://space.bilibili.com/29125536), [明昭MZhao](https://space.bilibili.com/24807452) and [Keldos](https://github.com/Keldos-Li)\n\nDownload latest code from [GitHub](https://github.com/GaiZhenbiao/ChuanhuChatGPT)",
|
77 |
"切换亮暗色主题": "Switch light/dark theme",
|
78 |
"您的IP区域:未知。": "Your IP region: Unknown.",
|
79 |
"获取IP地理位置失败。原因:": "Failed to get IP location. Reason: ",
|
80 |
"。你仍然可以使用聊天功能。": ". You can still use the chat function.",
|
81 |
+
"您的IP区域:": "Your IP region: ",
|
82 |
+
"总结": "Summarize",
|
83 |
+
"生成内容总结中……": "Generating content summary...",
|
84 |
+
"由于下面的原因,Google 拒绝返回 PaLM 的回答:\n\n": "Due to the following reasons, Google refuses to provide an answer to PaLM: \n\n",
|
85 |
+
"---\n⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "---\n⚠️ To ensure the security of API-Key, please modify the network settings in the configuration file `config.json`.",
|
86 |
+
"网络参数": "Network parameter"
|
87 |
}
|
locale/ja_JP.json
CHANGED
@@ -32,24 +32,33 @@
|
|
32 |
"📝 导出为Markdown": "📝 Markdownでエクスポート",
|
33 |
"默认保存于history文件夹": "デフォルトでhistoryフォルダに保存されます",
|
34 |
"高级": "Advanced",
|
35 |
-
"# ⚠️ 务必谨慎更改
|
36 |
"参数": "パラメータ",
|
37 |
-
"
|
38 |
"用于定位滥用行为": "不正行為を特定するために使用されます",
|
39 |
"用户名": "ユーザー名",
|
40 |
-
"网络设置": "ネットワーク設定",
|
41 |
"在这里输入API-Host...": "API-Hostを入力してください...",
|
42 |
"🔄 切换API地址": "🔄 APIアドレスを切り替え",
|
43 |
-
"
|
44 |
-
"
|
45 |
"🔄 设置代理地址": "🔄 プロキシアドレスを設定",
|
46 |
-
"🔙
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
"川虎Chat 🚀": "川虎Chat 🚀",
|
48 |
"开始实时传输回答……": "ストリーム出力開始……",
|
49 |
"Token 计数: ": "Token数: ",
|
50 |
",本次对话累计消耗了 ": ", 今の会話で消費合計 ",
|
51 |
"**获取API使用情况失败**": "**API使用状況の取得に失敗しました**",
|
|
|
|
|
52 |
"**本月使用金额** ": "**今月の使用料金** ",
|
|
|
53 |
"获取API使用情况失败:": "API使用状況の取得に失敗しました:",
|
54 |
"API密钥更改为了": "APIキーが変更されました",
|
55 |
"JSON解析错误,收到的内容: ": "JSON解析エラー、受信内容: ",
|
@@ -64,10 +73,15 @@
|
|
64 |
"API key为空,请检查是否输入正确。": "APIキーが入力されていません。正しく入力されているか確認してください。",
|
65 |
"请输入对话内容。": "会話内容を入力してください。",
|
66 |
"账单信息不适用": "課金情報は対象外です",
|
67 |
-
"由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)
|
68 |
"切换亮暗色主题": "テーマの明暗切替",
|
69 |
"您的IP区域:未知。": "あなたのIPアドレス地域:不明",
|
70 |
"获取IP地理位置失败。原因:": "IPアドレス地域の取得に失敗しました。理由:",
|
71 |
"。你仍然可以使用聊天功能。": "。あなたはまだチャット機能を使用できます。",
|
72 |
-
"您的IP区域:": "あなたのIPアドレス地域:"
|
73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
32 |
"📝 导出为Markdown": "📝 Markdownでエクスポート",
|
33 |
"默认保存于history文件夹": "デフォルトでhistoryフォルダに保存されます",
|
34 |
"高级": "Advanced",
|
35 |
+
"# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ 変更には慎重に ⚠️",
|
36 |
"参数": "パラメータ",
|
37 |
+
"停止符,用英文逗号隔开...": "ここにストップ文字を英語のカンマで区切って入力してください...",
|
38 |
"用于定位滥用行为": "不正行為を特定するために使用されます",
|
39 |
"用户名": "ユーザー名",
|
|
|
40 |
"在这里输入API-Host...": "API-Hostを入力してください...",
|
41 |
"🔄 切换API地址": "🔄 APIアドレスを切り替え",
|
42 |
+
"未设置代理...": "代理が設定されていません...",
|
43 |
+
"代理地址": "プロキシアドレス",
|
44 |
"🔄 设置代理地址": "🔄 プロキシアドレスを設定",
|
45 |
+
"🔙 恢复默认网络设置": "🔙 ネットワーク設定のリセット",
|
46 |
+
"🔄 检查更新...": "🔄 アップデートをチェック...",
|
47 |
+
"取消": "キャンセル",
|
48 |
+
"更新": "アップデート",
|
49 |
+
"详情": "詳細",
|
50 |
+
"好": "はい",
|
51 |
+
"更新成功,请重启本程序": "更新が成功しました、このプログラムを再起動してください",
|
52 |
+
"更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "更新に失敗しました、[手動での更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)をお試しください。",
|
53 |
"川虎Chat 🚀": "川虎Chat 🚀",
|
54 |
"开始实时传输回答……": "ストリーム出力開始……",
|
55 |
"Token 计数: ": "Token数: ",
|
56 |
",本次对话累计消耗了 ": ", 今の会話で消費合計 ",
|
57 |
"**获取API使用情况失败**": "**API使用状況の取得に失敗しました**",
|
58 |
+
"**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**API使用状況の取得に失敗しました**、`config.json`に正しい`sensitive_id`を入力する必要があります",
|
59 |
+
"**获取API使用情况失败**,sensitive_id错误或已过期": "**API使用状況の取得に失敗しました**、sensitive_idが間違っているか、期限切れです",
|
60 |
"**本月使用金额** ": "**今月の使用料金** ",
|
61 |
+
"本月使用金额": "今月の使用料金",
|
62 |
"获取API使用情况失败:": "API使用状況の取得に失敗しました:",
|
63 |
"API密钥更改为了": "APIキーが変更されました",
|
64 |
"JSON解析错误,收到的内容: ": "JSON解析エラー、受信内容: ",
|
|
|
73 |
"API key为空,请检查是否输入正确。": "APIキーが入力されていません。正しく入力されているか確認してください。",
|
74 |
"请输入对话内容。": "会話内容を入力してください。",
|
75 |
"账单信息不适用": "課金情報は対象外です",
|
76 |
+
"由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "開発:Bilibili [土川虎虎虎](https://space.bilibili.com/29125536) と [明昭MZhao](https://space.bilibili.com/24807452) と [Keldos](https://github.com/Keldos-Li)\n\n最新コードは川虎Chatのサイトへ [GitHubプロジェクト](https://github.com/GaiZhenbiao/ChuanhuChatGPT)",
|
77 |
"切换亮暗色主题": "テーマの明暗切替",
|
78 |
"您的IP区域:未知。": "あなたのIPアドレス地域:不明",
|
79 |
"获取IP地理位置失败。原因:": "IPアドレス地域の取得に失敗しました。理由:",
|
80 |
"。你仍然可以使用聊天功能。": "。あなたはまだチャット機能を使用できます。",
|
81 |
+
"您的IP区域:": "あなたのIPアドレス地域:",
|
82 |
+
"总结": "要約する",
|
83 |
+
"生成内容总结中……": "コンテンツ概要を生成しています...",
|
84 |
+
"由于下面的原因,Google 拒绝返回 PaLM 的回答:\n\n": "Googleは以下の理由から、PaLMの回答を返すことを拒否しています:\n\n",
|
85 |
+
"---\n⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "---\n⚠️ APIキーの安全性を確保するために、`config.json`ファイルでネットワーク設定を変更してください。",
|
86 |
+
"网络参数": "ネットワークパラメータ"
|
87 |
+
}
|
locale/ko_KR.json
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"未命名对话历史记录": "이름없는 대화 기록",
|
3 |
+
"在这里输入": "여기에 입력하세요",
|
4 |
+
"🧹 新的对话": "🧹 새로운 대화",
|
5 |
+
"🔄 重新生成": "🔄 재생성",
|
6 |
+
"🗑️ 删除最旧对话": "🗑️ 가장 오래된 대화 삭제",
|
7 |
+
"🗑️ 删除最新对话": "🗑️ 최신 대화 삭제",
|
8 |
+
"🗑️ 删除": "🗑️ 삭제",
|
9 |
+
"模型": "LLM 모델",
|
10 |
+
"多账号模式已开启,无需输入key,可直接开始对话": "다중 계정 모드가 활성화되어 있으므로 키를 입력할 필요가 없이 바로 대화를 시작할 수 있습니다",
|
11 |
+
"**发送消息** 或 **提交key** 以显示额度": "**메세지를 전송** 하거나 **Key를 입력**하여 크레딧 표시",
|
12 |
+
"选择模型": "모델 선택",
|
13 |
+
"选择LoRA模型": "LoRA 모델 선택",
|
14 |
+
"实时传输回答": "실시간 전송",
|
15 |
+
"单轮对话": "단일 대화",
|
16 |
+
"使用在线搜索": "온라인 검색 사용",
|
17 |
+
"选择回复语言(针对搜索&索引功能)": "답장 언어 선택 (검색 & 인덱스용)",
|
18 |
+
"上传索引文件": "업로드",
|
19 |
+
"双栏pdf": "2-column pdf",
|
20 |
+
"识别公式": "formula OCR",
|
21 |
+
"在这里输入System Prompt...": "여기에 시스템 프롬프트를 입력하세요...",
|
22 |
+
"加载Prompt模板": "프롬프트 템플릿 불러오기",
|
23 |
+
"选择Prompt模板集合文件": "프롬프트 콜렉션 파일 선택",
|
24 |
+
"🔄 刷新": "🔄 새로고침",
|
25 |
+
"从Prompt模板中加载": "프롬프트 템플릿에서 불러오기",
|
26 |
+
"保存/加载": "저장/불러오기",
|
27 |
+
"保存/加载对话历史记录": "대화 기록 저장/불러오기",
|
28 |
+
"从列表中加载对话": "리스트에서 대화 불러오기",
|
29 |
+
"设置文件名: 默认为.json,可选为.md": "파일 이름 설정: 기본값: .json, 선택: .md",
|
30 |
+
"设置保存文件名": "저장 파일명 설정",
|
31 |
+
"对话历史记录": "대화 기록",
|
32 |
+
"💾 保存对话": "💾 대화 저장",
|
33 |
+
"📝 导出为Markdown": "📝 마크다운으로 내보내기",
|
34 |
+
"默认保存于history文件夹": "히스토리 폴더에 기본 저장",
|
35 |
+
"高级": "고급",
|
36 |
+
"# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ 주의: 변경시 주의하세요. ⚠️",
|
37 |
+
"参数": "파라미터들",
|
38 |
+
"停止符,用英文逗号隔开...": "여기에 정지 토큰 입력, ','로 구분됨...",
|
39 |
+
"用于定位滥用行为": "악용 사례 파악에 활용됨",
|
40 |
+
"用户名": "사용자 이름",
|
41 |
+
"在这里输入API-Host...": "여기에 API host를 입력하세요...",
|
42 |
+
"🔄 切换API地址": "🔄 API 주소 변경",
|
43 |
+
"未设置代理...": "대리인이 설정되지 않았습니다...",
|
44 |
+
"代理地址": "프록시 주소",
|
45 |
+
"🔄 设置代理地址": "🔄 프록시 주소 설정",
|
46 |
+
"🔙 恢复默认网络设置": "🔙 네트워크 설정 초기화",
|
47 |
+
"🔄 检查更新...": "🔄 업데이트 확인...",
|
48 |
+
"取消": "취소",
|
49 |
+
"更新": "업데이트",
|
50 |
+
"详情": "상세",
|
51 |
+
"好": "예",
|
52 |
+
"更新成功,请重启本程序": "업데이트 성공, 이 프로그램을 재시작 해주세요",
|
53 |
+
"更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "업데이트 실패, [수동 업데이트](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)를 시도하십시오",
|
54 |
+
"川虎Chat 🚀": "Chuanhu Chat 🚀",
|
55 |
+
"开始实时传输回答……": "실시간 응답 출력 시작...",
|
56 |
+
"Token 计数: ": "토큰 수: ",
|
57 |
+
",本次对话累计消耗了 ": ",이 대화의 전체 비용은 ",
|
58 |
+
"**获取API使用情况失败**": "**API 사용량 가져오기 실패**",
|
59 |
+
"**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**API 사용량 가져오기 실패**. `config.json`에 올바른 `sensitive_id`를 입력해야 합니다",
|
60 |
+
"**获取API使用情况失败**,sensitive_id错误或已过期": "**API 사용량 가져오기 실패**. sensitive_id가 잘못되었거나 만료되었습니다",
|
61 |
+
"**本月使用金额** ": "**이번 달 사용금액** ",
|
62 |
+
"本月使用金额": "이번 달 사용금액",
|
63 |
+
"获取API使用情况失败:": "API 사용량 가져오기 실패:",
|
64 |
+
"API密钥更改为了": "API 키가 변경되었습니다.",
|
65 |
+
"JSON解析错误,收到的内容: ": "JSON 파싱 에러, 응답: ",
|
66 |
+
"模型设置为了:": "설정된 모델: ",
|
67 |
+
"☹️发生了错误:": "☹️에러: ",
|
68 |
+
"获取对话时发生错误,请查看后台日志": "대화를 가져오는 중 에러가 발생했습니다. 백그라운드 로그를 확인하세요",
|
69 |
+
"请检查网络连接,或者API-Key是否有效。": "네트워크 연결 또는 API키가 유효한지 확인하세요",
|
70 |
+
"连接超时,无法获取对话。": "연결 시간 초과, 대화를 가져올 수 없습니다.",
|
71 |
+
"读取超时,无法获取对话。": "읽기 시간 초과, 대화를 가져올 수 없습니다.",
|
72 |
+
"代理错误,无法获取对话。": "프록시 에러, 대화를 가져올 수 없습니다.",
|
73 |
+
"SSL错误,无法获取对话。": "SSL 에러, 대화를 가져올 수 없습니다.",
|
74 |
+
"API key为空,请检查是否输入正确。": "API 키가 비어 있습니다. 올바르게 입력되었는지 확인하십세요.",
|
75 |
+
"请输入对话内容。": "대화 내용을 입력하세요.",
|
76 |
+
"账单信息不适用": "청구 정보를 가져올 수 없습니다",
|
77 |
+
"由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "제작: Bilibili [土川虎虎虎](https://space.bilibili.com/29125536), [明昭MZhao](https://space.bilibili.com/24807452), [Keldos](https://github.com/Keldos-Li)\n\n최신 코드 다운로드: [GitHub](https://github.com/GaiZhenbiao/ChuanhuChatGPT)",
|
78 |
+
"切换亮暗色主题": "라이트/다크 테마 전환",
|
79 |
+
"您的IP区域:未知。": "IP 지역: 알 수 없음.",
|
80 |
+
"获取IP地理位置失败。原因:": "다음과 같은 이유로 IP 위치를 가져올 수 없습니다. 이유: ",
|
81 |
+
"。你仍然可以使用聊天功能。": ". 채팅 기능을 계속 사용할 수 있습니다.",
|
82 |
+
"您的IP区域:": "당신의 IP 지역: ",
|
83 |
+
"总结": "요약",
|
84 |
+
"生成内容总结中……": "콘텐츠 요약 생성중...",
|
85 |
+
"上传": "업로드",
|
86 |
+
"由于下面的原因,Google 拒绝返回 PaLM 的回答:\n\n": "구글은 다음과 같은 이유로 인해 PaLM의 응답을 거부합니다: \n\n",
|
87 |
+
"---\n⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "---\n⚠️ API-Key의 안전을 보장하기 위해 네트워크 설정을 `config.json` 구성 파일에서 수정해주세요.",
|
88 |
+
"网络参数": "네트워크 매개변수"
|
89 |
+
}
|
locale/sv-SE.json
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"未命名对话历史记录": "Onämnd Dialoghistorik",
|
3 |
+
"在这里输入": "Skriv in här",
|
4 |
+
"🧹 新的对话": "🧹 Ny Dialog",
|
5 |
+
"🔄 重新生成": "🔄 Regenerera",
|
6 |
+
"🗑️ 删除最旧对话": "🗑️ Ta bort äldsta dialogen",
|
7 |
+
"🗑️ 删除最新对话": "🗑️ Ta bort senaste dialogen",
|
8 |
+
"模型": "Modell",
|
9 |
+
"多账号模式已开启,无需输入key,可直接开始对话": "Flerkontoläge är aktiverat, ingen nyckel behövs, du kan starta dialogen direkt",
|
10 |
+
"**发送消息** 或 **提交key** 以显示额度": "**Skicka meddelande** eller **Skicka in nyckel** för att visa kredit",
|
11 |
+
"选择模型": "Välj Modell",
|
12 |
+
"选择LoRA模型": "Välj LoRA Modell",
|
13 |
+
"实时传输回答": "Strömmande utdata",
|
14 |
+
"单轮对话": "Enkel dialog",
|
15 |
+
"使用在线搜索": "Använd online-sökning",
|
16 |
+
"选择回复语言(针对搜索&索引功能)": "Välj svarspråk (för sök- och indexfunktion)",
|
17 |
+
"上传索引文件": "Ladda upp",
|
18 |
+
"双栏pdf": "Två-kolumns pdf",
|
19 |
+
"识别公式": "Formel OCR",
|
20 |
+
"在这里输入System Prompt...": "Skriv in System Prompt här...",
|
21 |
+
"加载Prompt模板": "Ladda Prompt-mall",
|
22 |
+
"选择Prompt模板集合文件": "Välj Prompt-mall Samlingsfil",
|
23 |
+
"🔄 刷新": "🔄 Uppdatera",
|
24 |
+
"从Prompt模板中加载": "Ladda från Prompt-mall",
|
25 |
+
"保存/加载": "Spara/Ladda",
|
26 |
+
"保存/加载对话历史记录": "Spara/Ladda Dialoghistorik",
|
27 |
+
"从列表中加载对话": "Ladda dialog från lista",
|
28 |
+
"设置文件名: 默认为.json,可选为.md": "Ställ in filnamn: standard är .json, valfritt är .md",
|
29 |
+
"设置保存文件名": "Ställ in sparfilnamn",
|
30 |
+
"对话历史记录": "Dialoghistorik",
|
31 |
+
"💾 保存对话": "💾 Spara Dialog",
|
32 |
+
"📝 导出为Markdown": "📝 Exportera som Markdown",
|
33 |
+
"默认保存于history文件夹": "Sparas som standard i mappen history",
|
34 |
+
"高级": "Avancerat",
|
35 |
+
"# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ Var försiktig med ändringar. ⚠️",
|
36 |
+
"参数": "Parametrar",
|
37 |
+
"停止符,用英文逗号隔开...": "Skriv in stopptecken här, separerade med kommatecken...",
|
38 |
+
"用于定位滥用行为": "Används för att lokalisera missbruk",
|
39 |
+
"用户名": "Användarnamn",
|
40 |
+
"在这里输入API-Host...": "Skriv in API-Host här...",
|
41 |
+
"🔄 切换API地址": "🔄 Byt API-adress",
|
42 |
+
"未设置代理...": "Inte inställd proxy...",
|
43 |
+
"代理地址": "Proxyadress",
|
44 |
+
"🔄 设置代理地址": "🔄 Ställ in Proxyadress",
|
45 |
+
"🔙 恢复网络默认设置": "🔙 Återställ Nätverksinställningar",
|
46 |
+
"🔄 检查更新...": "🔄 Sök efter uppdateringar...",
|
47 |
+
"取消": "Avbryt",
|
48 |
+
"更新": "Uppdatera",
|
49 |
+
"详情": "Detaljer",
|
50 |
+
"好": "OK",
|
51 |
+
"更新成功,请重启本程序": "Uppdaterat framgångsrikt, starta om programmet",
|
52 |
+
"更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "Uppdateringen misslyckades, prova att [uppdatera manuellt](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)",
|
53 |
+
"川虎Chat 🚀": "Chuanhu Chat 🚀",
|
54 |
+
"开始实时传输回答……": "Börjar strömma utdata...",
|
55 |
+
"Token 计数: ": "Tokenräkning: ",
|
56 |
+
",本次对话累计消耗了 ": ", Total kostnad för denna dialog är ",
|
57 |
+
"**获取API使用情况失败**": "**Misslyckades med att hämta API-användning**",
|
58 |
+
"**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**Misslyckades med att hämta API-användning**, korrekt sensitive_id behövs i `config.json`",
|
59 |
+
"**获取API使用情况失败**,sensitive_id错误或已过期": "**Misslyckades med att hämta API-användning**, felaktig eller utgången sensitive_id",
|
60 |
+
"**本月使用金额** ": "**Månadens användning** ",
|
61 |
+
"本月使用金额": "Månadens användning",
|
62 |
+
"获取API使用情况失败:": "Misslyckades med att hämta API-användning:",
|
63 |
+
"API密钥更改为了": "API-nyckeln har ändrats till",
|
64 |
+
"JSON解析错误,收到的内容: ": "JSON-tolkningsfel, mottaget innehåll: ",
|
65 |
+
"模型设置为了:": "Modellen är inställd på: ",
|
66 |
+
"☹️发生了错误:": "☹️Fel: ",
|
67 |
+
"获取对话时发生错误,请查看后台日志": "Ett fel uppstod när dialogen hämtades, kontrollera bakgrundsloggen",
|
68 |
+
"请检查网络连接,或者API-Key是否有效。": "Kontrollera nätverksanslutningen eller om API-nyckeln är giltig.",
|
69 |
+
"连接超时,无法获取对话。": "Anslutningen tog för lång tid, kunde inte hämta dialogen.",
|
70 |
+
"读取超时,无法获取对话。": "Läsningen tog för lång tid, kunde inte hämta dialogen.",
|
71 |
+
"代理错误,无法获取对话。": "Proxyfel, kunde inte hämta dialogen.",
|
72 |
+
"SSL错误,无法获取对话。": "SSL-fel, kunde inte hämta dialogen.",
|
73 |
+
"API key为空,请检查是否输入正确。": "API-nyckeln är tom, kontrollera om den är korrekt inmatad.",
|
74 |
+
"请输入对话内容。": "Ange dialoginnehåll.",
|
75 |
+
"账单信息不适用": "Faktureringsinformation är inte tillämplig",
|
76 |
+
"由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "Utvecklad av Bilibili [土川虎虎虎](https://space.bilibili.com/29125536), [明昭MZhao](https://space.bilibili.com/24807452) och [Keldos](https://github.com/Keldos-Li)\n\nLadda ner senaste koden från [GitHub](https://github.com/GaiZhenbiao/ChuanhuChatGPT)",
|
77 |
+
"切换亮暗色主题": "Byt ljus/mörk tema",
|
78 |
+
"您的IP区域:未知。": "Din IP-region: Okänd.",
|
79 |
+
"获取IP地理位置失败。原因:": "Misslyckades med att hämta IP-plats. Orsak: ",
|
80 |
+
"。你仍然可以使用聊天功能。": ". Du kan fortfarande använda chattfunktionen.",
|
81 |
+
"您的IP区域:": "Din IP-region: ",
|
82 |
+
"总结": "Sammanfatta",
|
83 |
+
"生成内容总结中……": "Genererar innehållssammanfattning...",
|
84 |
+
"由于下面的原因,Google 拒绝返回 PaLM 的回答:\n\n": "På grund av följande skäl vägrar Google att ge ett svar till PaLM: \n\n",
|
85 |
+
"---\n⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "---\n⚠️ För att säkerställa säkerheten för API-nyckeln, vänligen ändra nätverksinställningarna i konfigurationsfilen `config.json`.",
|
86 |
+
"网络参数": "nätverksparametrar"
|
87 |
+
}
|
modules/.DS_Store
ADDED
Binary file (6.15 kB). View file
|
|
modules/__pycache__/config.cpython-311.pyc
CHANGED
Binary files a/modules/__pycache__/config.cpython-311.pyc and b/modules/__pycache__/config.cpython-311.pyc differ
|
|
modules/__pycache__/config.cpython-39.pyc
CHANGED
Binary files a/modules/__pycache__/config.cpython-39.pyc and b/modules/__pycache__/config.cpython-39.pyc differ
|
|
modules/__pycache__/index_func.cpython-311.pyc
CHANGED
Binary files a/modules/__pycache__/index_func.cpython-311.pyc and b/modules/__pycache__/index_func.cpython-311.pyc differ
|
|
modules/__pycache__/index_func.cpython-39.pyc
CHANGED
Binary files a/modules/__pycache__/index_func.cpython-39.pyc and b/modules/__pycache__/index_func.cpython-39.pyc differ
|
|
modules/__pycache__/overwrites.cpython-311.pyc
CHANGED
Binary files a/modules/__pycache__/overwrites.cpython-311.pyc and b/modules/__pycache__/overwrites.cpython-311.pyc differ
|
|
modules/__pycache__/overwrites.cpython-39.pyc
CHANGED
Binary files a/modules/__pycache__/overwrites.cpython-39.pyc and b/modules/__pycache__/overwrites.cpython-39.pyc differ
|
|
modules/__pycache__/pdf_func.cpython-311.pyc
CHANGED
Binary files a/modules/__pycache__/pdf_func.cpython-311.pyc and b/modules/__pycache__/pdf_func.cpython-311.pyc differ
|
|
modules/__pycache__/presets.cpython-311.pyc
CHANGED
Binary files a/modules/__pycache__/presets.cpython-311.pyc and b/modules/__pycache__/presets.cpython-311.pyc differ
|
|
modules/__pycache__/presets.cpython-39.pyc
CHANGED
Binary files a/modules/__pycache__/presets.cpython-39.pyc and b/modules/__pycache__/presets.cpython-39.pyc differ
|
|
modules/__pycache__/repo.cpython-311.pyc
ADDED
Binary file (14.1 kB). View file
|
|
modules/__pycache__/shared.cpython-311.pyc
CHANGED
Binary files a/modules/__pycache__/shared.cpython-311.pyc and b/modules/__pycache__/shared.cpython-311.pyc differ
|
|
modules/__pycache__/shared.cpython-39.pyc
CHANGED
Binary files a/modules/__pycache__/shared.cpython-39.pyc and b/modules/__pycache__/shared.cpython-39.pyc differ
|
|
modules/__pycache__/train_func.cpython-311.pyc
ADDED
Binary file (11.7 kB). View file
|
|
modules/__pycache__/utils.cpython-311.pyc
CHANGED
Binary files a/modules/__pycache__/utils.cpython-311.pyc and b/modules/__pycache__/utils.cpython-311.pyc differ
|
|
modules/__pycache__/utils.cpython-39.pyc
CHANGED
Binary files a/modules/__pycache__/utils.cpython-39.pyc and b/modules/__pycache__/utils.cpython-39.pyc differ
|
|
modules/__pycache__/webui.cpython-311.pyc
ADDED
Binary file (5.45 kB). View file
|
|
modules/config.py
CHANGED
@@ -11,11 +11,11 @@ from . import presets
|
|
11 |
|
12 |
__all__ = [
|
13 |
"my_api_key",
|
|
|
14 |
"authflag",
|
15 |
"auth_list",
|
16 |
"dockerflag",
|
17 |
"retrieve_proxy",
|
18 |
-
"log_level",
|
19 |
"advance_docs",
|
20 |
"update_doc_config",
|
21 |
"usage_limit",
|
@@ -23,8 +23,11 @@ __all__ = [
|
|
23 |
"server_name",
|
24 |
"server_port",
|
25 |
"share",
|
|
|
|
|
26 |
"hide_history_when_not_logged_in",
|
27 |
-
"default_chuanhu_assistant_model"
|
|
|
28 |
]
|
29 |
|
30 |
# 添加一个统一的config文件,避免文件过多造成的疑惑(优先级最低)
|
@@ -35,10 +38,22 @@ if os.path.exists("config.json"):
|
|
35 |
else:
|
36 |
config = {}
|
37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
lang_config = config.get("language", "auto")
|
39 |
language = os.environ.get("LANGUAGE", lang_config)
|
40 |
|
41 |
-
hide_history_when_not_logged_in = config.get(
|
|
|
|
|
|
|
|
|
42 |
|
43 |
if os.path.exists("api_key.txt"):
|
44 |
logging.info("检测到api_key.txt文件,正在进行迁移...")
|
@@ -52,26 +67,44 @@ if os.path.exists("auth.json"):
|
|
52 |
logging.info("检测到auth.json文件,正在进行迁移...")
|
53 |
auth_list = []
|
54 |
with open("auth.json", "r", encoding='utf-8') as f:
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
config["users"] = auth_list
|
63 |
os.rename("auth.json", "auth(deprecated).json")
|
64 |
with open("config.json", "w", encoding='utf-8') as f:
|
65 |
json.dump(config, f, indent=4, ensure_ascii=False)
|
66 |
|
67 |
-
|
68 |
dockerflag = config.get("dockerflag", False)
|
69 |
if os.environ.get("dockerrun") == "yes":
|
70 |
dockerflag = True
|
71 |
|
72 |
-
|
73 |
my_api_key = config.get("openai_api_key", "")
|
74 |
my_api_key = os.environ.get("OPENAI_API_KEY", my_api_key)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
|
76 |
xmchat_api_key = config.get("xmchat_api_key", "")
|
77 |
os.environ["XMCHAT_API_KEY"] = xmchat_api_key
|
@@ -81,11 +114,23 @@ os.environ["MINIMAX_API_KEY"] = minimax_api_key
|
|
81 |
minimax_group_id = config.get("minimax_group_id", "")
|
82 |
os.environ["MINIMAX_GROUP_ID"] = minimax_group_id
|
83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
usage_limit = os.environ.get("USAGE_LIMIT", config.get("usage_limit", 120))
|
86 |
|
87 |
-
|
88 |
-
multi_api_key = config.get("multi_api_key", False)
|
89 |
if multi_api_key:
|
90 |
api_key_list = config.get("api_key_list", [])
|
91 |
if len(api_key_list) == 0:
|
@@ -93,21 +138,26 @@ if multi_api_key:
|
|
93 |
sys.exit(1)
|
94 |
shared.state.set_api_key_queue(api_key_list)
|
95 |
|
96 |
-
auth_list = config.get("users", [])
|
97 |
authflag = len(auth_list) > 0 # 是否开启认证的状态值,改为判断auth_list长度
|
98 |
|
99 |
# 处理自定义的api_host,优先读环境变量的配置,如果存在则自动装配
|
100 |
-
api_host = os.environ.get(
|
|
|
101 |
if api_host is not None:
|
102 |
shared.state.set_api_host(api_host)
|
|
|
|
|
103 |
|
104 |
-
default_chuanhu_assistant_model = config.get(
|
|
|
105 |
for x in ["GOOGLE_CSE_ID", "GOOGLE_API_KEY", "WOLFRAM_ALPHA_APPID", "SERPAPI_API_KEY"]:
|
106 |
if config.get(x, None) is not None:
|
107 |
os.environ[x] = config[x]
|
108 |
|
|
|
109 |
@contextmanager
|
110 |
-
def retrieve_openai_api(api_key
|
111 |
old_api_key = os.environ.get("OPENAI_API_KEY", "")
|
112 |
if api_key is None:
|
113 |
os.environ["OPENAI_API_KEY"] = my_api_key
|
@@ -117,24 +167,20 @@ def retrieve_openai_api(api_key = None):
|
|
117 |
yield api_key
|
118 |
os.environ["OPENAI_API_KEY"] = old_api_key
|
119 |
|
120 |
-
## 处理log
|
121 |
-
log_level = config.get("log_level", "INFO")
|
122 |
-
logging.basicConfig(
|
123 |
-
level=log_level,
|
124 |
-
format="%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s",
|
125 |
-
)
|
126 |
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
|
|
132 |
|
133 |
# 重置系统变量,在不需要设置的时候不设置环境变量,以免引起全局代理报错
|
134 |
os.environ["HTTP_PROXY"] = ""
|
135 |
os.environ["HTTPS_PROXY"] = ""
|
136 |
|
137 |
-
local_embedding = config.get("local_embedding", False)
|
|
|
138 |
|
139 |
@contextmanager
|
140 |
def retrieve_proxy(proxy=None):
|
@@ -151,22 +197,62 @@ def retrieve_proxy(proxy=None):
|
|
151 |
old_var = os.environ["HTTP_PROXY"], os.environ["HTTPS_PROXY"]
|
152 |
os.environ["HTTP_PROXY"] = http_proxy
|
153 |
os.environ["HTTPS_PROXY"] = https_proxy
|
154 |
-
yield http_proxy, https_proxy
|
155 |
|
156 |
# return old proxy
|
157 |
os.environ["HTTP_PROXY"], os.environ["HTTPS_PROXY"] = old_var
|
158 |
|
159 |
|
160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
advance_docs = defaultdict(lambda: defaultdict(dict))
|
162 |
advance_docs.update(config.get("advance_docs", {}))
|
|
|
|
|
163 |
def update_doc_config(two_column_pdf):
|
164 |
global advance_docs
|
165 |
advance_docs["pdf"]["two_column"] = two_column_pdf
|
166 |
|
167 |
logging.info(f"更新后的文件参数为:{advance_docs}")
|
168 |
|
169 |
-
|
|
|
170 |
server_name = config.get("server_name", None)
|
171 |
server_port = config.get("server_port", None)
|
172 |
if server_name is None:
|
@@ -188,3 +274,7 @@ except ValueError:
|
|
188 |
pass
|
189 |
|
190 |
share = config.get("share", False)
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
__all__ = [
|
13 |
"my_api_key",
|
14 |
+
"sensitive_id",
|
15 |
"authflag",
|
16 |
"auth_list",
|
17 |
"dockerflag",
|
18 |
"retrieve_proxy",
|
|
|
19 |
"advance_docs",
|
20 |
"update_doc_config",
|
21 |
"usage_limit",
|
|
|
23 |
"server_name",
|
24 |
"server_port",
|
25 |
"share",
|
26 |
+
"check_update",
|
27 |
+
"latex_delimiters_set",
|
28 |
"hide_history_when_not_logged_in",
|
29 |
+
"default_chuanhu_assistant_model",
|
30 |
+
"show_api_billing"
|
31 |
]
|
32 |
|
33 |
# 添加一个统一的config文件,避免文件过多造成的疑惑(优先级最低)
|
|
|
38 |
else:
|
39 |
config = {}
|
40 |
|
41 |
+
|
42 |
+
def load_config_to_environ(key_list):
|
43 |
+
global config
|
44 |
+
for key in key_list:
|
45 |
+
if key in config:
|
46 |
+
os.environ[key.upper()] = os.environ.get(key.upper(), config[key])
|
47 |
+
|
48 |
+
|
49 |
lang_config = config.get("language", "auto")
|
50 |
language = os.environ.get("LANGUAGE", lang_config)
|
51 |
|
52 |
+
hide_history_when_not_logged_in = config.get(
|
53 |
+
"hide_history_when_not_logged_in", False)
|
54 |
+
check_update = config.get("check_update", True)
|
55 |
+
show_api_billing = config.get("show_api_billing", False)
|
56 |
+
show_api_billing = bool(os.environ.get("SHOW_API_BILLING", show_api_billing))
|
57 |
|
58 |
if os.path.exists("api_key.txt"):
|
59 |
logging.info("检测到api_key.txt文件,正在进行迁移...")
|
|
|
67 |
logging.info("检测到auth.json文件,正在进行迁移...")
|
68 |
auth_list = []
|
69 |
with open("auth.json", "r", encoding='utf-8') as f:
|
70 |
+
auth = json.load(f)
|
71 |
+
for _ in auth:
|
72 |
+
if auth[_]["username"] and auth[_]["password"]:
|
73 |
+
auth_list.append((auth[_]["username"], auth[_]["password"]))
|
74 |
+
else:
|
75 |
+
logging.error("请检查auth.json文件中的用户名和密码!")
|
76 |
+
sys.exit(1)
|
77 |
config["users"] = auth_list
|
78 |
os.rename("auth.json", "auth(deprecated).json")
|
79 |
with open("config.json", "w", encoding='utf-8') as f:
|
80 |
json.dump(config, f, indent=4, ensure_ascii=False)
|
81 |
|
82 |
+
# 处理docker if we are running in Docker
|
83 |
dockerflag = config.get("dockerflag", False)
|
84 |
if os.environ.get("dockerrun") == "yes":
|
85 |
dockerflag = True
|
86 |
|
87 |
+
# 处理 api-key 以及 允许的用户列表
|
88 |
my_api_key = config.get("openai_api_key", "")
|
89 |
my_api_key = os.environ.get("OPENAI_API_KEY", my_api_key)
|
90 |
+
os.environ["OPENAI_API_KEY"] = my_api_key
|
91 |
+
os.environ["OPENAI_EMBEDDING_API_KEY"] = my_api_key
|
92 |
+
|
93 |
+
if config.get("legacy_api_usage", False):
|
94 |
+
sensitive_id = my_api_key
|
95 |
+
else:
|
96 |
+
sensitive_id = config.get("sensitive_id", "")
|
97 |
+
sensitive_id = os.environ.get("SENSITIVE_ID", sensitive_id)
|
98 |
+
|
99 |
+
# 模型配置
|
100 |
+
if "extra_models" in config:
|
101 |
+
presets.MODELS.extend(config["extra_models"])
|
102 |
+
logging.info(f"已添加额外的模型:{config['extra_models']}")
|
103 |
+
|
104 |
+
google_palm_api_key = config.get("google_palm_api_key", "")
|
105 |
+
google_palm_api_key = os.environ.get(
|
106 |
+
"GOOGLE_PALM_API_KEY", google_palm_api_key)
|
107 |
+
os.environ["GOOGLE_PALM_API_KEY"] = google_palm_api_key
|
108 |
|
109 |
xmchat_api_key = config.get("xmchat_api_key", "")
|
110 |
os.environ["XMCHAT_API_KEY"] = xmchat_api_key
|
|
|
114 |
minimax_group_id = config.get("minimax_group_id", "")
|
115 |
os.environ["MINIMAX_GROUP_ID"] = minimax_group_id
|
116 |
|
117 |
+
midjourney_proxy_api_base = config.get("midjourney_proxy_api_base", "")
|
118 |
+
os.environ["MIDJOURNEY_PROXY_API_BASE"] = midjourney_proxy_api_base
|
119 |
+
midjourney_proxy_api_secret = config.get("midjourney_proxy_api_secret", "")
|
120 |
+
os.environ["MIDJOURNEY_PROXY_API_SECRET"] = midjourney_proxy_api_secret
|
121 |
+
midjourney_discord_proxy_url = config.get("midjourney_discord_proxy_url", "")
|
122 |
+
os.environ["MIDJOURNEY_DISCORD_PROXY_URL"] = midjourney_discord_proxy_url
|
123 |
+
midjourney_temp_folder = config.get("midjourney_temp_folder", "")
|
124 |
+
os.environ["MIDJOURNEY_TEMP_FOLDER"] = midjourney_temp_folder
|
125 |
+
|
126 |
+
load_config_to_environ(["openai_api_type", "azure_openai_api_key", "azure_openai_api_base_url",
|
127 |
+
"azure_openai_api_version", "azure_deployment_name", "azure_embedding_deployment_name", "azure_embedding_model_name"])
|
128 |
+
|
129 |
|
130 |
usage_limit = os.environ.get("USAGE_LIMIT", config.get("usage_limit", 120))
|
131 |
|
132 |
+
# 多账户机制
|
133 |
+
multi_api_key = config.get("multi_api_key", False) # 是否开启多账户机制
|
134 |
if multi_api_key:
|
135 |
api_key_list = config.get("api_key_list", [])
|
136 |
if len(api_key_list) == 0:
|
|
|
138 |
sys.exit(1)
|
139 |
shared.state.set_api_key_queue(api_key_list)
|
140 |
|
141 |
+
auth_list = config.get("users", []) # 实际上是使用者的列表
|
142 |
authflag = len(auth_list) > 0 # 是否开启认证的状态值,改为判断auth_list长度
|
143 |
|
144 |
# 处理自定义的api_host,优先读环境变量的配置,如果存在则自动装配
|
145 |
+
api_host = os.environ.get(
|
146 |
+
"OPENAI_API_BASE", config.get("openai_api_base", None))
|
147 |
if api_host is not None:
|
148 |
shared.state.set_api_host(api_host)
|
149 |
+
os.environ["OPENAI_API_BASE"] = f"{api_host}/v1"
|
150 |
+
logging.info(f"OpenAI API Base set to: {os.environ['OPENAI_API_BASE']}")
|
151 |
|
152 |
+
default_chuanhu_assistant_model = config.get(
|
153 |
+
"default_chuanhu_assistant_model", "gpt-3.5-turbo")
|
154 |
for x in ["GOOGLE_CSE_ID", "GOOGLE_API_KEY", "WOLFRAM_ALPHA_APPID", "SERPAPI_API_KEY"]:
|
155 |
if config.get(x, None) is not None:
|
156 |
os.environ[x] = config[x]
|
157 |
|
158 |
+
|
159 |
@contextmanager
|
160 |
+
def retrieve_openai_api(api_key=None):
|
161 |
old_api_key = os.environ.get("OPENAI_API_KEY", "")
|
162 |
if api_key is None:
|
163 |
os.environ["OPENAI_API_KEY"] = my_api_key
|
|
|
167 |
yield api_key
|
168 |
os.environ["OPENAI_API_KEY"] = old_api_key
|
169 |
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
|
171 |
+
|
172 |
+
# 处理代理:
|
173 |
+
http_proxy = os.environ.get("HTTP_PROXY", "")
|
174 |
+
https_proxy = os.environ.get("HTTPS_PROXY", "")
|
175 |
+
http_proxy = config.get("http_proxy", http_proxy)
|
176 |
+
https_proxy = config.get("https_proxy", https_proxy)
|
177 |
|
178 |
# 重置系统变量,在不需要设置的时候不设置环境变量,以免引起全局代理报错
|
179 |
os.environ["HTTP_PROXY"] = ""
|
180 |
os.environ["HTTPS_PROXY"] = ""
|
181 |
|
182 |
+
local_embedding = config.get("local_embedding", False) # 是否使用本地embedding
|
183 |
+
|
184 |
|
185 |
@contextmanager
|
186 |
def retrieve_proxy(proxy=None):
|
|
|
197 |
old_var = os.environ["HTTP_PROXY"], os.environ["HTTPS_PROXY"]
|
198 |
os.environ["HTTP_PROXY"] = http_proxy
|
199 |
os.environ["HTTPS_PROXY"] = https_proxy
|
200 |
+
yield http_proxy, https_proxy # return new proxy
|
201 |
|
202 |
# return old proxy
|
203 |
os.environ["HTTP_PROXY"], os.environ["HTTPS_PROXY"] = old_var
|
204 |
|
205 |
|
206 |
+
# 处理latex options
|
207 |
+
user_latex_option = config.get("latex_option", "default")
|
208 |
+
if user_latex_option == "default":
|
209 |
+
latex_delimiters_set = [
|
210 |
+
{"left": "$$", "right": "$$", "display": True},
|
211 |
+
{"left": "$", "right": "$", "display": False},
|
212 |
+
{"left": "\\(", "right": "\\)", "display": False},
|
213 |
+
{"left": "\\[", "right": "\\]", "display": True},
|
214 |
+
]
|
215 |
+
elif user_latex_option == "strict":
|
216 |
+
latex_delimiters_set = [
|
217 |
+
{"left": "$$", "right": "$$", "display": True},
|
218 |
+
{"left": "\\(", "right": "\\)", "display": False},
|
219 |
+
{"left": "\\[", "right": "\\]", "display": True},
|
220 |
+
]
|
221 |
+
elif user_latex_option == "all":
|
222 |
+
latex_delimiters_set = [
|
223 |
+
{"left": "$$", "right": "$$", "display": True},
|
224 |
+
{"left": "$", "right": "$", "display": False},
|
225 |
+
{"left": "\\(", "right": "\\)", "display": False},
|
226 |
+
{"left": "\\[", "right": "\\]", "display": True},
|
227 |
+
{"left": "\\begin{equation}", "right": "\\end{equation}", "display": True},
|
228 |
+
{"left": "\\begin{align}", "right": "\\end{align}", "display": True},
|
229 |
+
{"left": "\\begin{alignat}", "right": "\\end{alignat}", "display": True},
|
230 |
+
{"left": "\\begin{gather}", "right": "\\end{gather}", "display": True},
|
231 |
+
{"left": "\\begin{CD}", "right": "\\end{CD}", "display": True},
|
232 |
+
]
|
233 |
+
elif user_latex_option == "disabled":
|
234 |
+
latex_delimiters_set = []
|
235 |
+
else:
|
236 |
+
latex_delimiters_set = [
|
237 |
+
{"left": "$$", "right": "$$", "display": True},
|
238 |
+
{"left": "$", "right": "$", "display": False},
|
239 |
+
{"left": "\\(", "right": "\\)", "display": False},
|
240 |
+
{"left": "\\[", "right": "\\]", "display": True},
|
241 |
+
]
|
242 |
+
|
243 |
+
# 处理advance docs
|
244 |
advance_docs = defaultdict(lambda: defaultdict(dict))
|
245 |
advance_docs.update(config.get("advance_docs", {}))
|
246 |
+
|
247 |
+
|
248 |
def update_doc_config(two_column_pdf):
|
249 |
global advance_docs
|
250 |
advance_docs["pdf"]["two_column"] = two_column_pdf
|
251 |
|
252 |
logging.info(f"更新后的文件参数为:{advance_docs}")
|
253 |
|
254 |
+
|
255 |
+
# 处理gradio.launch参数
|
256 |
server_name = config.get("server_name", None)
|
257 |
server_port = config.get("server_port", None)
|
258 |
if server_name is None:
|
|
|
274 |
pass
|
275 |
|
276 |
share = config.get("share", False)
|
277 |
+
|
278 |
+
# avatar
|
279 |
+
bot_avatar = config.get("bot_avatar", "default")
|
280 |
+
user_avatar = config.get("user_avatar", "default")
|
modules/index_func.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
import os
|
2 |
import logging
|
3 |
|
4 |
-
import
|
5 |
import PyPDF2
|
6 |
from tqdm import tqdm
|
7 |
|
@@ -10,19 +10,6 @@ from modules.utils import *
|
|
10 |
from modules.config import local_embedding
|
11 |
|
12 |
|
13 |
-
def get_index_name(file_src):
|
14 |
-
file_paths = [x.name for x in file_src]
|
15 |
-
file_paths.sort(key=lambda x: os.path.basename(x))
|
16 |
-
|
17 |
-
md5_hash = hashlib.md5()
|
18 |
-
for file_path in file_paths:
|
19 |
-
with open(file_path, "rb") as f:
|
20 |
-
while chunk := f.read(8192):
|
21 |
-
md5_hash.update(chunk)
|
22 |
-
|
23 |
-
return md5_hash.hexdigest()
|
24 |
-
|
25 |
-
|
26 |
def get_documents(file_src):
|
27 |
from langchain.schema import Document
|
28 |
from langchain.text_splitter import TokenTextSplitter
|
@@ -47,11 +34,12 @@ def get_documents(file_src):
|
|
47 |
pdftext = parse_pdf(filepath, two_column).text
|
48 |
except:
|
49 |
pdftext = ""
|
50 |
-
with open(filepath, "rb"
|
51 |
pdfReader = PyPDF2.PdfReader(pdfFileObj)
|
52 |
for page in tqdm(pdfReader.pages):
|
53 |
pdftext += page.extract_text()
|
54 |
-
texts = [Document(page_content=pdftext,
|
|
|
55 |
elif file_type == ".docx":
|
56 |
logging.debug("Loading Word...")
|
57 |
from langchain.document_loaders import UnstructuredWordDocumentLoader
|
@@ -72,7 +60,8 @@ def get_documents(file_src):
|
|
72 |
text_list = excel_to_string(filepath)
|
73 |
texts = []
|
74 |
for elem in text_list:
|
75 |
-
texts.append(Document(page_content=elem,
|
|
|
76 |
else:
|
77 |
logging.debug("Loading text file...")
|
78 |
from langchain.document_loaders import TextLoader
|
@@ -111,14 +100,20 @@ def construct_index(
|
|
111 |
embedding_limit = None if embedding_limit == 0 else embedding_limit
|
112 |
separator = " " if separator == "" else separator
|
113 |
|
114 |
-
index_name =
|
115 |
index_path = f"./index/{index_name}"
|
116 |
if local_embedding:
|
117 |
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
|
118 |
-
embeddings = HuggingFaceEmbeddings(
|
|
|
119 |
else:
|
120 |
from langchain.embeddings import OpenAIEmbeddings
|
121 |
-
|
|
|
|
|
|
|
|
|
|
|
122 |
if os.path.exists(index_path):
|
123 |
logging.info("找到了缓存的索引文件,加载中……")
|
124 |
return FAISS.load_local(index_path, embeddings)
|
|
|
1 |
import os
|
2 |
import logging
|
3 |
|
4 |
+
import hashlib
|
5 |
import PyPDF2
|
6 |
from tqdm import tqdm
|
7 |
|
|
|
10 |
from modules.config import local_embedding
|
11 |
|
12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
def get_documents(file_src):
|
14 |
from langchain.schema import Document
|
15 |
from langchain.text_splitter import TokenTextSplitter
|
|
|
34 |
pdftext = parse_pdf(filepath, two_column).text
|
35 |
except:
|
36 |
pdftext = ""
|
37 |
+
with open(filepath, "rb") as pdfFileObj:
|
38 |
pdfReader = PyPDF2.PdfReader(pdfFileObj)
|
39 |
for page in tqdm(pdfReader.pages):
|
40 |
pdftext += page.extract_text()
|
41 |
+
texts = [Document(page_content=pdftext,
|
42 |
+
metadata={"source": filepath})]
|
43 |
elif file_type == ".docx":
|
44 |
logging.debug("Loading Word...")
|
45 |
from langchain.document_loaders import UnstructuredWordDocumentLoader
|
|
|
60 |
text_list = excel_to_string(filepath)
|
61 |
texts = []
|
62 |
for elem in text_list:
|
63 |
+
texts.append(Document(page_content=elem,
|
64 |
+
metadata={"source": filepath}))
|
65 |
else:
|
66 |
logging.debug("Loading text file...")
|
67 |
from langchain.document_loaders import TextLoader
|
|
|
100 |
embedding_limit = None if embedding_limit == 0 else embedding_limit
|
101 |
separator = " " if separator == "" else separator
|
102 |
|
103 |
+
index_name = get_file_hash(file_src)
|
104 |
index_path = f"./index/{index_name}"
|
105 |
if local_embedding:
|
106 |
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
|
107 |
+
embeddings = HuggingFaceEmbeddings(
|
108 |
+
model_name="sentence-transformers/distiluse-base-multilingual-cased-v2")
|
109 |
else:
|
110 |
from langchain.embeddings import OpenAIEmbeddings
|
111 |
+
if os.environ.get("OPENAI_API_TYPE", "openai") == "openai":
|
112 |
+
embeddings = OpenAIEmbeddings(openai_api_base=os.environ.get(
|
113 |
+
"OPENAI_API_BASE", None), openai_api_key=os.environ.get("OPENAI_EMBEDDING_API_KEY", api_key))
|
114 |
+
else:
|
115 |
+
embeddings = OpenAIEmbeddings(deployment=os.environ["AZURE_EMBEDDING_DEPLOYMENT_NAME"], openai_api_key=os.environ["AZURE_OPENAI_API_KEY"],
|
116 |
+
model=os.environ["AZURE_EMBEDDING_MODEL_NAME"], openai_api_base=os.environ["AZURE_OPENAI_API_BASE_URL"], openai_api_type="azure")
|
117 |
if os.path.exists(index_path):
|
118 |
logging.info("找到了缓存的索引文件,加载中……")
|
119 |
return FAISS.load_local(index_path, embeddings)
|
modules/models/Google_PaLM.py
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from .base_model import BaseLLMModel
|
2 |
+
import google.generativeai as palm
|
3 |
+
|
4 |
+
class Google_PaLM_Client(BaseLLMModel):
|
5 |
+
def __init__(self, model_name, api_key, user_name="") -> None:
|
6 |
+
super().__init__(model_name=model_name, user=user_name)
|
7 |
+
self.api_key = api_key
|
8 |
+
|
9 |
+
def _get_palm_style_input(self):
|
10 |
+
new_history = []
|
11 |
+
for item in self.history:
|
12 |
+
if item["role"] == "user":
|
13 |
+
new_history.append({'author': '1', 'content': item["content"]})
|
14 |
+
else:
|
15 |
+
new_history.append({'author': '0', 'content': item["content"]})
|
16 |
+
return new_history
|
17 |
+
|
18 |
+
def get_answer_at_once(self):
|
19 |
+
palm.configure(api_key=self.api_key)
|
20 |
+
messages = self._get_palm_style_input()
|
21 |
+
response = palm.chat(context=self.system_prompt, messages=messages, temperature=self.temperature, top_p=self.top_p)
|
22 |
+
if response.last is not None:
|
23 |
+
return response.last, len(response.last)
|
24 |
+
else:
|
25 |
+
reasons = '\n\n'.join(reason['reason'].name for reason in response.filters)
|
26 |
+
return "由于下面的原因,Google 拒绝返回 PaLM 的回答:\n\n" + reasons, 0
|
modules/models/__pycache__/ChuanhuAgent.cpython-311.pyc
CHANGED
Binary files a/modules/models/__pycache__/ChuanhuAgent.cpython-311.pyc and b/modules/models/__pycache__/ChuanhuAgent.cpython-311.pyc differ
|
|
modules/models/__pycache__/Google_PaLM.cpython-311.pyc
ADDED
Binary file (2.64 kB). View file
|
|
modules/models/__pycache__/azure.cpython-311.pyc
ADDED
Binary file (1.18 kB). View file
|
|
modules/models/__pycache__/base_model.cpython-311.pyc
CHANGED
Binary files a/modules/models/__pycache__/base_model.cpython-311.pyc and b/modules/models/__pycache__/base_model.cpython-311.pyc differ
|
|
modules/models/__pycache__/base_model.cpython-39.pyc
CHANGED
Binary files a/modules/models/__pycache__/base_model.cpython-39.pyc and b/modules/models/__pycache__/base_model.cpython-39.pyc differ
|
|
modules/models/__pycache__/models.cpython-311.pyc
CHANGED
Binary files a/modules/models/__pycache__/models.cpython-311.pyc and b/modules/models/__pycache__/models.cpython-311.pyc differ
|
|
modules/models/__pycache__/models.cpython-39.pyc
CHANGED
Binary files a/modules/models/__pycache__/models.cpython-39.pyc and b/modules/models/__pycache__/models.cpython-39.pyc differ
|
|
modules/models/azure.py
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain.chat_models import AzureChatOpenAI
|
2 |
+
import os
|
3 |
+
|
4 |
+
from .base_model import Base_Chat_Langchain_Client
|
5 |
+
|
6 |
+
# load_config_to_environ(["azure_openai_api_key", "azure_api_base_url", "azure_openai_api_version", "azure_deployment_name"])
|
7 |
+
|
8 |
+
class Azure_OpenAI_Client(Base_Chat_Langchain_Client):
|
9 |
+
def setup_model(self):
|
10 |
+
# inplement this to setup the model then return it
|
11 |
+
return AzureChatOpenAI(
|
12 |
+
openai_api_base=os.environ["AZURE_OPENAI_API_BASE_URL"],
|
13 |
+
openai_api_version=os.environ["AZURE_OPENAI_API_VERSION"],
|
14 |
+
deployment_name=os.environ["AZURE_DEPLOYMENT_NAME"],
|
15 |
+
openai_api_key=os.environ["AZURE_OPENAI_API_KEY"],
|
16 |
+
openai_api_type="azure",
|
17 |
+
)
|
modules/models/base_model.py
CHANGED
@@ -29,6 +29,8 @@ from langchain.input import print_text
|
|
29 |
from langchain.schema import AgentAction, AgentFinish, LLMResult
|
30 |
from threading import Thread, Condition
|
31 |
from collections import deque
|
|
|
|
|
32 |
|
33 |
from ..presets import *
|
34 |
from ..index_func import *
|
@@ -36,6 +38,7 @@ from ..utils import *
|
|
36 |
from .. import shared
|
37 |
from ..config import retrieve_proxy
|
38 |
|
|
|
39 |
class CallbackToIterator:
|
40 |
def __init__(self):
|
41 |
self.queue = deque()
|
@@ -52,7 +55,8 @@ class CallbackToIterator:
|
|
52 |
|
53 |
def __next__(self):
|
54 |
with self.cond:
|
55 |
-
|
|
|
56 |
self.cond.wait()
|
57 |
if not self.queue:
|
58 |
raise StopIteration()
|
@@ -63,6 +67,7 @@ class CallbackToIterator:
|
|
63 |
self.finished = True
|
64 |
self.cond.notify() # Wake up the generator if it's waiting.
|
65 |
|
|
|
66 |
def get_action_description(text):
|
67 |
match = re.search('```(.*?)```', text, re.S)
|
68 |
json_text = match.group(1)
|
@@ -72,10 +77,11 @@ def get_action_description(text):
|
|
72 |
action_name = json_dict['action']
|
73 |
action_input = json_dict['action_input']
|
74 |
if action_name != "Final Answer":
|
75 |
-
return f'
|
76 |
else:
|
77 |
return ""
|
78 |
|
|
|
79 |
class ChuanhuCallbackHandler(BaseCallbackHandler):
|
80 |
|
81 |
def __init__(self, callback) -> None:
|
@@ -117,6 +123,10 @@ class ChuanhuCallbackHandler(BaseCallbackHandler):
|
|
117 |
"""Run on new LLM token. Only available when streaming is enabled."""
|
118 |
self.callback(token)
|
119 |
|
|
|
|
|
|
|
|
|
120 |
|
121 |
class ModelType(Enum):
|
122 |
Unknown = -1
|
@@ -129,6 +139,9 @@ class ModelType(Enum):
|
|
129 |
YuanAI = 6
|
130 |
Minimax = 7
|
131 |
ChuanhuAgent = 8
|
|
|
|
|
|
|
132 |
|
133 |
@classmethod
|
134 |
def get_type(cls, model_name: str):
|
@@ -152,6 +165,12 @@ class ModelType(Enum):
|
|
152 |
model_type = ModelType.Minimax
|
153 |
elif "川虎助理" in model_name_lower:
|
154 |
model_type = ModelType.ChuanhuAgent
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
else:
|
156 |
model_type = ModelType.Unknown
|
157 |
return model_type
|
@@ -161,7 +180,7 @@ class BaseLLMModel:
|
|
161 |
def __init__(
|
162 |
self,
|
163 |
model_name,
|
164 |
-
system_prompt=
|
165 |
temperature=1.0,
|
166 |
top_p=1.0,
|
167 |
n_choices=1,
|
@@ -201,7 +220,8 @@ class BaseLLMModel:
|
|
201 |
conversations are stored in self.history, with the most recent question, in OpenAI format
|
202 |
should return a generator, each time give the next word (str) in the answer
|
203 |
"""
|
204 |
-
logging.warning(
|
|
|
205 |
response, _ = self.get_answer_at_once()
|
206 |
yield response
|
207 |
|
@@ -212,7 +232,8 @@ class BaseLLMModel:
|
|
212 |
the answer (str)
|
213 |
total token count (int)
|
214 |
"""
|
215 |
-
logging.warning(
|
|
|
216 |
response_iter = self.get_answer_stream_iter()
|
217 |
count = 0
|
218 |
for response in response_iter:
|
@@ -246,7 +267,8 @@ class BaseLLMModel:
|
|
246 |
stream_iter = self.get_answer_stream_iter()
|
247 |
|
248 |
if display_append:
|
249 |
-
display_append =
|
|
|
250 |
for partial_text in stream_iter:
|
251 |
chatbot[-1] = (chatbot[-1][0], partial_text + display_append)
|
252 |
self.all_token_counts[-1] += 1
|
@@ -273,9 +295,11 @@ class BaseLLMModel:
|
|
273 |
self.history[-2] = construct_user(fake_input)
|
274 |
chatbot[-1] = (chatbot[-1][0], ai_reply + display_append)
|
275 |
if fake_input is not None:
|
276 |
-
self.all_token_counts[-1] += count_token(
|
|
|
277 |
else:
|
278 |
-
self.all_token_counts[-1] = total_token_count -
|
|
|
279 |
status_text = self.token_message()
|
280 |
return chatbot, status_text
|
281 |
|
@@ -299,10 +323,13 @@ class BaseLLMModel:
|
|
299 |
from langchain.chat_models import ChatOpenAI
|
300 |
from langchain.callbacks import StdOutCallbackHandler
|
301 |
prompt_template = "Write a concise summary of the following:\n\n{text}\n\nCONCISE SUMMARY IN " + language + ":"
|
302 |
-
PROMPT = PromptTemplate(
|
|
|
303 |
llm = ChatOpenAI()
|
304 |
-
chain = load_summarize_chain(
|
305 |
-
|
|
|
|
|
306 |
print(i18n("总结") + f": {summary}")
|
307 |
chatbot.append([i18n("上传了")+str(len(files))+"个文件", summary])
|
308 |
return chatbot, status
|
@@ -323,9 +350,12 @@ class BaseLLMModel:
|
|
323 |
msg = "索引获取成功,生成回答中……"
|
324 |
logging.info(msg)
|
325 |
with retrieve_proxy():
|
326 |
-
retriever = VectorStoreRetriever(vectorstore=index, search_type="similarity_score_threshold",search_kwargs={
|
327 |
-
|
328 |
-
|
|
|
|
|
|
|
329 |
reference_results = add_source_numbers(reference_results)
|
330 |
display_append = add_details(reference_results)
|
331 |
display_append = "\n\n" + "".join(display_append)
|
@@ -348,10 +378,12 @@ class BaseLLMModel:
|
|
348 |
reference_results.append([result['body'], result['href']])
|
349 |
display_append.append(
|
350 |
# f"{idx+1}. [{domain_name}]({result['href']})\n"
|
351 |
-
f"<
|
352 |
)
|
353 |
reference_results = add_source_numbers(reference_results)
|
354 |
-
display_append = "<ol>\n\n" + "".join(display_append) + "</ol>"
|
|
|
|
|
355 |
real_inputs = (
|
356 |
replace_today(WEBSEARCH_PTOMPT_TEMPLATE)
|
357 |
.replace("{query}", real_inputs)
|
@@ -375,14 +407,16 @@ class BaseLLMModel:
|
|
375 |
|
376 |
status_text = "开始生成回答……"
|
377 |
logging.info(
|
378 |
-
|
|
|
379 |
)
|
380 |
if should_check_token_count:
|
381 |
yield chatbot + [(inputs, "")], status_text
|
382 |
if reply_language == "跟随问题语言(不稳定)":
|
383 |
reply_language = "the same language as the question, such as English, 中文, 日本語, Español, Français, or Deutsch."
|
384 |
|
385 |
-
limited_context, fake_inputs, display_append, inputs, chatbot = self.prepare_inputs(
|
|
|
386 |
yield chatbot + [(fake_inputs, "")], status_text
|
387 |
|
388 |
if (
|
@@ -434,7 +468,7 @@ class BaseLLMModel:
|
|
434 |
yield chatbot, status_text
|
435 |
except Exception as e:
|
436 |
traceback.print_exc()
|
437 |
-
status_text = STANDARD_ERROR_MSG + str(e)
|
438 |
yield chatbot, status_text
|
439 |
|
440 |
if len(self.history) > 1 and self.history[-1]["content"] != inputs:
|
@@ -568,10 +602,13 @@ class BaseLLMModel:
|
|
568 |
self.system_prompt = new_system_prompt
|
569 |
|
570 |
def set_key(self, new_access_key):
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
|
|
|
|
|
|
575 |
|
576 |
def set_single_turn(self, new_single_turn):
|
577 |
self.single_turn = new_single_turn
|
@@ -580,7 +617,8 @@ class BaseLLMModel:
|
|
580 |
self.history = []
|
581 |
self.all_token_counts = []
|
582 |
self.interrupted = False
|
583 |
-
pathlib.Path(os.path.join(HISTORY_DIR, self.user_identifier, new_auto_history_filename(
|
|
|
584 |
return [], self.token_message([0])
|
585 |
|
586 |
def delete_first_conversation(self):
|
@@ -623,7 +661,8 @@ class BaseLLMModel:
|
|
623 |
|
624 |
def auto_save(self, chatbot):
|
625 |
history_file_path = get_history_filepath(self.user_identifier)
|
626 |
-
save_file(history_file_path, self.system_prompt,
|
|
|
627 |
|
628 |
def export_markdown(self, filename, chatbot, user_name):
|
629 |
if filename == "":
|
@@ -639,7 +678,8 @@ class BaseLLMModel:
|
|
639 |
filename = filename.name
|
640 |
try:
|
641 |
if "/" not in filename:
|
642 |
-
history_file_path = os.path.join(
|
|
|
643 |
else:
|
644 |
history_file_path = filename
|
645 |
with open(history_file_path, "r", encoding="utf-8") as f:
|
@@ -665,15 +705,33 @@ class BaseLLMModel:
|
|
665 |
logging.info(f"没有找到对话历史记录 {filename}")
|
666 |
return gr.update(), self.system_prompt, gr.update()
|
667 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
668 |
def auto_load(self):
|
669 |
if self.user_identifier == "":
|
670 |
self.reset()
|
671 |
return self.system_prompt, gr.update()
|
672 |
history_file_path = get_history_filepath(self.user_identifier)
|
673 |
-
filename, system_prompt, chatbot = self.load_chat_history(
|
|
|
674 |
return system_prompt, chatbot
|
675 |
|
676 |
-
|
677 |
def like(self):
|
678 |
"""like the last response, implement if needed
|
679 |
"""
|
@@ -683,3 +741,47 @@ class BaseLLMModel:
|
|
683 |
"""dislike the last response, implement if needed
|
684 |
"""
|
685 |
return gr.update()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
from langchain.schema import AgentAction, AgentFinish, LLMResult
|
30 |
from threading import Thread, Condition
|
31 |
from collections import deque
|
32 |
+
from langchain.chat_models.base import BaseChatModel
|
33 |
+
from langchain.schema import HumanMessage, AIMessage, SystemMessage, BaseMessage
|
34 |
|
35 |
from ..presets import *
|
36 |
from ..index_func import *
|
|
|
38 |
from .. import shared
|
39 |
from ..config import retrieve_proxy
|
40 |
|
41 |
+
|
42 |
class CallbackToIterator:
|
43 |
def __init__(self):
|
44 |
self.queue = deque()
|
|
|
55 |
|
56 |
def __next__(self):
|
57 |
with self.cond:
|
58 |
+
# Wait for a value to be added to the queue.
|
59 |
+
while not self.queue and not self.finished:
|
60 |
self.cond.wait()
|
61 |
if not self.queue:
|
62 |
raise StopIteration()
|
|
|
67 |
self.finished = True
|
68 |
self.cond.notify() # Wake up the generator if it's waiting.
|
69 |
|
70 |
+
|
71 |
def get_action_description(text):
|
72 |
match = re.search('```(.*?)```', text, re.S)
|
73 |
json_text = match.group(1)
|
|
|
77 |
action_name = json_dict['action']
|
78 |
action_input = json_dict['action_input']
|
79 |
if action_name != "Final Answer":
|
80 |
+
return f'<!-- S O PREFIX --><p class="agent-prefix">{action_name}: {action_input}\n\n</p><!-- E O PREFIX -->'
|
81 |
else:
|
82 |
return ""
|
83 |
|
84 |
+
|
85 |
class ChuanhuCallbackHandler(BaseCallbackHandler):
|
86 |
|
87 |
def __init__(self, callback) -> None:
|
|
|
123 |
"""Run on new LLM token. Only available when streaming is enabled."""
|
124 |
self.callback(token)
|
125 |
|
126 |
+
def on_chat_model_start(self, serialized: Dict[str, Any], messages: List[List[BaseMessage]], **kwargs: Any) -> Any:
|
127 |
+
"""Run when a chat model starts running."""
|
128 |
+
pass
|
129 |
+
|
130 |
|
131 |
class ModelType(Enum):
|
132 |
Unknown = -1
|
|
|
139 |
YuanAI = 6
|
140 |
Minimax = 7
|
141 |
ChuanhuAgent = 8
|
142 |
+
GooglePaLM = 9
|
143 |
+
LangchainChat = 10
|
144 |
+
Midjourney = 11
|
145 |
|
146 |
@classmethod
|
147 |
def get_type(cls, model_name: str):
|
|
|
165 |
model_type = ModelType.Minimax
|
166 |
elif "川虎助理" in model_name_lower:
|
167 |
model_type = ModelType.ChuanhuAgent
|
168 |
+
elif "palm" in model_name_lower:
|
169 |
+
model_type = ModelType.GooglePaLM
|
170 |
+
elif "midjourney" in model_name_lower:
|
171 |
+
model_type = ModelType.Midjourney
|
172 |
+
elif "azure" in model_name_lower or "api" in model_name_lower:
|
173 |
+
model_type = ModelType.LangchainChat
|
174 |
else:
|
175 |
model_type = ModelType.Unknown
|
176 |
return model_type
|
|
|
180 |
def __init__(
|
181 |
self,
|
182 |
model_name,
|
183 |
+
system_prompt=INITIAL_SYSTEM_PROMPT,
|
184 |
temperature=1.0,
|
185 |
top_p=1.0,
|
186 |
n_choices=1,
|
|
|
220 |
conversations are stored in self.history, with the most recent question, in OpenAI format
|
221 |
should return a generator, each time give the next word (str) in the answer
|
222 |
"""
|
223 |
+
logging.warning(
|
224 |
+
"stream predict not implemented, using at once predict instead")
|
225 |
response, _ = self.get_answer_at_once()
|
226 |
yield response
|
227 |
|
|
|
232 |
the answer (str)
|
233 |
total token count (int)
|
234 |
"""
|
235 |
+
logging.warning(
|
236 |
+
"at once predict not implemented, using stream predict instead")
|
237 |
response_iter = self.get_answer_stream_iter()
|
238 |
count = 0
|
239 |
for response in response_iter:
|
|
|
267 |
stream_iter = self.get_answer_stream_iter()
|
268 |
|
269 |
if display_append:
|
270 |
+
display_append = '\n\n<hr class="append-display no-in-raw" />' + display_append
|
271 |
+
partial_text = ""
|
272 |
for partial_text in stream_iter:
|
273 |
chatbot[-1] = (chatbot[-1][0], partial_text + display_append)
|
274 |
self.all_token_counts[-1] += 1
|
|
|
295 |
self.history[-2] = construct_user(fake_input)
|
296 |
chatbot[-1] = (chatbot[-1][0], ai_reply + display_append)
|
297 |
if fake_input is not None:
|
298 |
+
self.all_token_counts[-1] += count_token(
|
299 |
+
construct_assistant(ai_reply))
|
300 |
else:
|
301 |
+
self.all_token_counts[-1] = total_token_count - \
|
302 |
+
sum(self.all_token_counts)
|
303 |
status_text = self.token_message()
|
304 |
return chatbot, status_text
|
305 |
|
|
|
323 |
from langchain.chat_models import ChatOpenAI
|
324 |
from langchain.callbacks import StdOutCallbackHandler
|
325 |
prompt_template = "Write a concise summary of the following:\n\n{text}\n\nCONCISE SUMMARY IN " + language + ":"
|
326 |
+
PROMPT = PromptTemplate(
|
327 |
+
template=prompt_template, input_variables=["text"])
|
328 |
llm = ChatOpenAI()
|
329 |
+
chain = load_summarize_chain(
|
330 |
+
llm, chain_type="map_reduce", return_intermediate_steps=True, map_prompt=PROMPT, combine_prompt=PROMPT)
|
331 |
+
summary = chain({"input_documents": list(index.docstore.__dict__[
|
332 |
+
"_dict"].values())}, return_only_outputs=True)["output_text"]
|
333 |
print(i18n("总结") + f": {summary}")
|
334 |
chatbot.append([i18n("上传了")+str(len(files))+"个文件", summary])
|
335 |
return chatbot, status
|
|
|
350 |
msg = "索引获取成功,生成回答中……"
|
351 |
logging.info(msg)
|
352 |
with retrieve_proxy():
|
353 |
+
retriever = VectorStoreRetriever(vectorstore=index, search_type="similarity_score_threshold", search_kwargs={
|
354 |
+
"k": 6, "score_threshold": 0.5})
|
355 |
+
relevant_documents = retriever.get_relevant_documents(
|
356 |
+
real_inputs)
|
357 |
+
reference_results = [[d.page_content.strip("�"), os.path.basename(
|
358 |
+
d.metadata["source"])] for d in relevant_documents]
|
359 |
reference_results = add_source_numbers(reference_results)
|
360 |
display_append = add_details(reference_results)
|
361 |
display_append = "\n\n" + "".join(display_append)
|
|
|
378 |
reference_results.append([result['body'], result['href']])
|
379 |
display_append.append(
|
380 |
# f"{idx+1}. [{domain_name}]({result['href']})\n"
|
381 |
+
f"<a href=\"{result['href']}\" target=\"_blank\">{idx+1}. {result['title']}</a>"
|
382 |
)
|
383 |
reference_results = add_source_numbers(reference_results)
|
384 |
+
# display_append = "<ol>\n\n" + "".join(display_append) + "</ol>"
|
385 |
+
display_append = '<div class = "source-a">' + \
|
386 |
+
"".join(display_append) + '</div>'
|
387 |
real_inputs = (
|
388 |
replace_today(WEBSEARCH_PTOMPT_TEMPLATE)
|
389 |
.replace("{query}", real_inputs)
|
|
|
407 |
|
408 |
status_text = "开始生成回答……"
|
409 |
logging.info(
|
410 |
+
"用户" + f"{self.user_identifier}" + "的输入为:" +
|
411 |
+
colorama.Fore.BLUE + f"{inputs}" + colorama.Style.RESET_ALL
|
412 |
)
|
413 |
if should_check_token_count:
|
414 |
yield chatbot + [(inputs, "")], status_text
|
415 |
if reply_language == "跟随问题语言(不稳定)":
|
416 |
reply_language = "the same language as the question, such as English, 中文, 日本語, Español, Français, or Deutsch."
|
417 |
|
418 |
+
limited_context, fake_inputs, display_append, inputs, chatbot = self.prepare_inputs(
|
419 |
+
real_inputs=inputs, use_websearch=use_websearch, files=files, reply_language=reply_language, chatbot=chatbot)
|
420 |
yield chatbot + [(fake_inputs, "")], status_text
|
421 |
|
422 |
if (
|
|
|
468 |
yield chatbot, status_text
|
469 |
except Exception as e:
|
470 |
traceback.print_exc()
|
471 |
+
status_text = STANDARD_ERROR_MSG + beautify_err_msg(str(e))
|
472 |
yield chatbot, status_text
|
473 |
|
474 |
if len(self.history) > 1 and self.history[-1]["content"] != inputs:
|
|
|
602 |
self.system_prompt = new_system_prompt
|
603 |
|
604 |
def set_key(self, new_access_key):
|
605 |
+
if "*" not in new_access_key:
|
606 |
+
self.api_key = new_access_key.strip()
|
607 |
+
msg = i18n("API密钥更改为了") + hide_middle_chars(self.api_key)
|
608 |
+
logging.info(msg)
|
609 |
+
return self.api_key, msg
|
610 |
+
else:
|
611 |
+
return gr.update(), gr.update()
|
612 |
|
613 |
def set_single_turn(self, new_single_turn):
|
614 |
self.single_turn = new_single_turn
|
|
|
617 |
self.history = []
|
618 |
self.all_token_counts = []
|
619 |
self.interrupted = False
|
620 |
+
pathlib.Path(os.path.join(HISTORY_DIR, self.user_identifier, new_auto_history_filename(
|
621 |
+
os.path.join(HISTORY_DIR, self.user_identifier)))).touch()
|
622 |
return [], self.token_message([0])
|
623 |
|
624 |
def delete_first_conversation(self):
|
|
|
661 |
|
662 |
def auto_save(self, chatbot):
|
663 |
history_file_path = get_history_filepath(self.user_identifier)
|
664 |
+
save_file(history_file_path, self.system_prompt,
|
665 |
+
self.history, chatbot, self.user_identifier)
|
666 |
|
667 |
def export_markdown(self, filename, chatbot, user_name):
|
668 |
if filename == "":
|
|
|
678 |
filename = filename.name
|
679 |
try:
|
680 |
if "/" not in filename:
|
681 |
+
history_file_path = os.path.join(
|
682 |
+
HISTORY_DIR, user_name, filename)
|
683 |
else:
|
684 |
history_file_path = filename
|
685 |
with open(history_file_path, "r", encoding="utf-8") as f:
|
|
|
705 |
logging.info(f"没有找到对话历史记录 {filename}")
|
706 |
return gr.update(), self.system_prompt, gr.update()
|
707 |
|
708 |
+
def delete_chat_history(self, filename, user_name):
|
709 |
+
if filename == "CANCELED":
|
710 |
+
return gr.update(), gr.update(), gr.update()
|
711 |
+
if filename == "":
|
712 |
+
return i18n("你没有选择任何对话历史"), gr.update(), gr.update()
|
713 |
+
if not filename.endswith(".json"):
|
714 |
+
filename += ".json"
|
715 |
+
if "/" not in filename:
|
716 |
+
history_file_path = os.path.join(HISTORY_DIR, user_name, filename)
|
717 |
+
else:
|
718 |
+
history_file_path = filename
|
719 |
+
try:
|
720 |
+
os.remove(history_file_path)
|
721 |
+
return i18n("删除对话历史成功"), get_history_names(False, user_name), []
|
722 |
+
except:
|
723 |
+
logging.info(f"删除对话历史失败 {history_file_path}")
|
724 |
+
return i18n("对话历史")+filename+i18n("已经被删除啦"), gr.update(), gr.update()
|
725 |
+
|
726 |
def auto_load(self):
|
727 |
if self.user_identifier == "":
|
728 |
self.reset()
|
729 |
return self.system_prompt, gr.update()
|
730 |
history_file_path = get_history_filepath(self.user_identifier)
|
731 |
+
filename, system_prompt, chatbot = self.load_chat_history(
|
732 |
+
history_file_path, self.user_identifier)
|
733 |
return system_prompt, chatbot
|
734 |
|
|
|
735 |
def like(self):
|
736 |
"""like the last response, implement if needed
|
737 |
"""
|
|
|
741 |
"""dislike the last response, implement if needed
|
742 |
"""
|
743 |
return gr.update()
|
744 |
+
|
745 |
+
|
746 |
+
class Base_Chat_Langchain_Client(BaseLLMModel):
|
747 |
+
def __init__(self, model_name, user_name=""):
|
748 |
+
super().__init__(model_name, user=user_name)
|
749 |
+
self.need_api_key = False
|
750 |
+
self.model = self.setup_model()
|
751 |
+
|
752 |
+
def setup_model(self):
|
753 |
+
# inplement this to setup the model then return it
|
754 |
+
pass
|
755 |
+
|
756 |
+
def _get_langchain_style_history(self):
|
757 |
+
history = [SystemMessage(content=self.system_prompt)]
|
758 |
+
for i in self.history:
|
759 |
+
if i["role"] == "user":
|
760 |
+
history.append(HumanMessage(content=i["content"]))
|
761 |
+
elif i["role"] == "assistant":
|
762 |
+
history.append(AIMessage(content=i["content"]))
|
763 |
+
return history
|
764 |
+
|
765 |
+
def get_answer_at_once(self):
|
766 |
+
assert isinstance(
|
767 |
+
self.model, BaseChatModel), "model is not instance of LangChain BaseChatModel"
|
768 |
+
history = self._get_langchain_style_history()
|
769 |
+
response = self.model.generate(history)
|
770 |
+
return response.content, sum(response.content)
|
771 |
+
|
772 |
+
def get_answer_stream_iter(self):
|
773 |
+
it = CallbackToIterator()
|
774 |
+
assert isinstance(
|
775 |
+
self.model, BaseChatModel), "model is not instance of LangChain BaseChatModel"
|
776 |
+
history = self._get_langchain_style_history()
|
777 |
+
|
778 |
+
def thread_func():
|
779 |
+
self.model(messages=history, callbacks=[
|
780 |
+
ChuanhuCallbackHandler(it.callback)])
|
781 |
+
it.finish()
|
782 |
+
t = Thread(target=thread_func)
|
783 |
+
t.start()
|
784 |
+
partial_text = ""
|
785 |
+
for value in it:
|
786 |
+
partial_text += value
|
787 |
+
yield partial_text
|
modules/models/midjourney.py
ADDED
@@ -0,0 +1,385 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import base64
|
2 |
+
import io
|
3 |
+
import json
|
4 |
+
import logging
|
5 |
+
import pathlib
|
6 |
+
import time
|
7 |
+
import tempfile
|
8 |
+
import os
|
9 |
+
|
10 |
+
from datetime import datetime
|
11 |
+
|
12 |
+
import requests
|
13 |
+
import tiktoken
|
14 |
+
from PIL import Image
|
15 |
+
|
16 |
+
from modules.config import retrieve_proxy
|
17 |
+
from modules.models.models import XMChat
|
18 |
+
|
19 |
+
mj_proxy_api_base = os.getenv("MIDJOURNEY_PROXY_API_BASE")
|
20 |
+
mj_discord_proxy_url = os.getenv("MIDJOURNEY_DISCORD_PROXY_URL")
|
21 |
+
mj_temp_folder = os.getenv("MIDJOURNEY_TEMP_FOLDER")
|
22 |
+
|
23 |
+
|
24 |
+
class Midjourney_Client(XMChat):
|
25 |
+
|
26 |
+
class FetchDataPack:
|
27 |
+
"""
|
28 |
+
A class to store data for current fetching data from Midjourney API
|
29 |
+
"""
|
30 |
+
|
31 |
+
action: str # current action, e.g. "IMAGINE", "UPSCALE", "VARIATION"
|
32 |
+
prefix_content: str # prefix content, task description and process hint
|
33 |
+
task_id: str # task id
|
34 |
+
start_time: float # task start timestamp
|
35 |
+
timeout: int # task timeout in seconds
|
36 |
+
finished: bool # whether the task is finished
|
37 |
+
prompt: str # prompt for the task
|
38 |
+
|
39 |
+
def __init__(self, action, prefix_content, task_id, timeout=900):
|
40 |
+
self.action = action
|
41 |
+
self.prefix_content = prefix_content
|
42 |
+
self.task_id = task_id
|
43 |
+
self.start_time = time.time()
|
44 |
+
self.timeout = timeout
|
45 |
+
self.finished = False
|
46 |
+
|
47 |
+
def __init__(self, model_name, api_key, user_name=""):
|
48 |
+
super().__init__(api_key, user_name)
|
49 |
+
self.model_name = model_name
|
50 |
+
self.history = []
|
51 |
+
self.api_key = api_key
|
52 |
+
self.headers = {
|
53 |
+
"Content-Type": "application/json",
|
54 |
+
"mj-api-secret": f"{api_key}"
|
55 |
+
}
|
56 |
+
self.proxy_url = mj_proxy_api_base
|
57 |
+
self.command_splitter = "::"
|
58 |
+
|
59 |
+
if mj_temp_folder:
|
60 |
+
temp = "./tmp"
|
61 |
+
if user_name:
|
62 |
+
temp = os.path.join(temp, user_name)
|
63 |
+
if not os.path.exists(temp):
|
64 |
+
os.makedirs(temp)
|
65 |
+
self.temp_path = tempfile.mkdtemp(dir=temp)
|
66 |
+
logging.info("mj temp folder: " + self.temp_path)
|
67 |
+
else:
|
68 |
+
self.temp_path = None
|
69 |
+
|
70 |
+
def use_mj_self_proxy_url(self, img_url):
|
71 |
+
"""
|
72 |
+
replace discord cdn url with mj self proxy url
|
73 |
+
"""
|
74 |
+
return img_url.replace(
|
75 |
+
"https://cdn.discordapp.com/",
|
76 |
+
mj_discord_proxy_url and mj_discord_proxy_url or "https://cdn.discordapp.com/"
|
77 |
+
)
|
78 |
+
|
79 |
+
def split_image(self, image_url):
|
80 |
+
"""
|
81 |
+
when enabling temp dir, split image into 4 parts
|
82 |
+
"""
|
83 |
+
with retrieve_proxy():
|
84 |
+
image_bytes = requests.get(image_url).content
|
85 |
+
img = Image.open(io.BytesIO(image_bytes))
|
86 |
+
width, height = img.size
|
87 |
+
# calculate half width and height
|
88 |
+
half_width = width // 2
|
89 |
+
half_height = height // 2
|
90 |
+
# create coordinates (top-left x, top-left y, bottom-right x, bottom-right y)
|
91 |
+
coordinates = [(0, 0, half_width, half_height),
|
92 |
+
(half_width, 0, width, half_height),
|
93 |
+
(0, half_height, half_width, height),
|
94 |
+
(half_width, half_height, width, height)]
|
95 |
+
|
96 |
+
images = [img.crop(c) for c in coordinates]
|
97 |
+
return images
|
98 |
+
|
99 |
+
def auth_mj(self):
|
100 |
+
"""
|
101 |
+
auth midjourney api
|
102 |
+
"""
|
103 |
+
# TODO: check if secret is valid
|
104 |
+
return {'status': 'ok'}
|
105 |
+
|
106 |
+
def request_mj(self, path: str, action: str, data: str, retries=3):
|
107 |
+
"""
|
108 |
+
request midjourney api
|
109 |
+
"""
|
110 |
+
mj_proxy_url = self.proxy_url
|
111 |
+
if mj_proxy_url is None or not (mj_proxy_url.startswith("http://") or mj_proxy_url.startswith("https://")):
|
112 |
+
raise Exception('please set MIDJOURNEY_PROXY_API_BASE in ENV or in config.json')
|
113 |
+
|
114 |
+
auth_ = self.auth_mj()
|
115 |
+
if auth_.get('error'):
|
116 |
+
raise Exception('auth not set')
|
117 |
+
|
118 |
+
fetch_url = f"{mj_proxy_url}/{path}"
|
119 |
+
# logging.info(f"[MJ Proxy] {action} {fetch_url} params: {data}")
|
120 |
+
|
121 |
+
for _ in range(retries):
|
122 |
+
try:
|
123 |
+
with retrieve_proxy():
|
124 |
+
res = requests.request(method=action, url=fetch_url, headers=self.headers, data=data)
|
125 |
+
break
|
126 |
+
except Exception as e:
|
127 |
+
print(e)
|
128 |
+
|
129 |
+
if res.status_code != 200:
|
130 |
+
raise Exception(f'{res.status_code} - {res.content}')
|
131 |
+
|
132 |
+
return res
|
133 |
+
|
134 |
+
def fetch_status(self, fetch_data: FetchDataPack):
|
135 |
+
"""
|
136 |
+
fetch status of current task
|
137 |
+
"""
|
138 |
+
if fetch_data.start_time + fetch_data.timeout < time.time():
|
139 |
+
fetch_data.finished = True
|
140 |
+
return "任务超时,请检查 dc 输出。描述:" + fetch_data.prompt
|
141 |
+
|
142 |
+
time.sleep(3)
|
143 |
+
status_res = self.request_mj(f"task/{fetch_data.task_id}/fetch", "GET", '')
|
144 |
+
status_res_json = status_res.json()
|
145 |
+
if not (200 <= status_res.status_code < 300):
|
146 |
+
raise Exception("任务状态获取失败:" + status_res_json.get(
|
147 |
+
'error') or status_res_json.get('description') or '未知错误')
|
148 |
+
else:
|
149 |
+
fetch_data.finished = False
|
150 |
+
if status_res_json['status'] == "SUCCESS":
|
151 |
+
content = status_res_json['imageUrl']
|
152 |
+
fetch_data.finished = True
|
153 |
+
elif status_res_json['status'] == "FAILED":
|
154 |
+
content = status_res_json['failReason'] or '未知原因'
|
155 |
+
fetch_data.finished = True
|
156 |
+
elif status_res_json['status'] == "NOT_START":
|
157 |
+
content = f'任务未开始,已等待 {time.time() - fetch_data.start_time:.2f} 秒'
|
158 |
+
elif status_res_json['status'] == "IN_PROGRESS":
|
159 |
+
content = '任务正在运行'
|
160 |
+
if status_res_json.get('progress'):
|
161 |
+
content += f",进度:{status_res_json['progress']}"
|
162 |
+
elif status_res_json['status'] == "SUBMITTED":
|
163 |
+
content = '任务已提交处理'
|
164 |
+
elif status_res_json['status'] == "FAILURE":
|
165 |
+
fetch_data.finished = True
|
166 |
+
return "任务处理失败,原因:" + status_res_json['failReason'] or '未知原因'
|
167 |
+
else:
|
168 |
+
content = status_res_json['status']
|
169 |
+
if fetch_data.finished:
|
170 |
+
img_url = self.use_mj_self_proxy_url(status_res_json['imageUrl'])
|
171 |
+
if fetch_data.action == "DESCRIBE":
|
172 |
+
return f"\n{status_res_json['prompt']}"
|
173 |
+
time_cost_str = f"\n\n{fetch_data.action} 花费时间:{time.time() - fetch_data.start_time:.2f} 秒"
|
174 |
+
upscale_str = ""
|
175 |
+
variation_str = ""
|
176 |
+
if fetch_data.action in ["IMAGINE", "UPSCALE", "VARIATION"]:
|
177 |
+
upscale = [f'/mj UPSCALE{self.command_splitter}{i+1}{self.command_splitter}{fetch_data.task_id}'
|
178 |
+
for i in range(4)]
|
179 |
+
upscale_str = '\n放大图片:\n\n' + '\n\n'.join(upscale)
|
180 |
+
variation = [f'/mj VARIATION{self.command_splitter}{i+1}{self.command_splitter}{fetch_data.task_id}'
|
181 |
+
for i in range(4)]
|
182 |
+
variation_str = '\n图片变体:\n\n' + '\n\n'.join(variation)
|
183 |
+
if self.temp_path and fetch_data.action in ["IMAGINE", "VARIATION"]:
|
184 |
+
try:
|
185 |
+
images = self.split_image(img_url)
|
186 |
+
# save images to temp path
|
187 |
+
for i in range(4):
|
188 |
+
images[i].save(pathlib.Path(self.temp_path) / f"{fetch_data.task_id}_{i}.png")
|
189 |
+
img_str = '\n'.join(
|
190 |
+
[f"![{fetch_data.task_id}](/file={self.temp_path}/{fetch_data.task_id}_{i}.png)"
|
191 |
+
for i in range(4)])
|
192 |
+
return fetch_data.prefix_content + f"{time_cost_str}\n\n{img_str}{upscale_str}{variation_str}"
|
193 |
+
except Exception as e:
|
194 |
+
logging.error(e)
|
195 |
+
return fetch_data.prefix_content + \
|
196 |
+
f"{time_cost_str}[![{fetch_data.task_id}]({img_url})]({img_url}){upscale_str}{variation_str}"
|
197 |
+
else:
|
198 |
+
content = f"**任务状态:** [{(datetime.now()).strftime('%Y-%m-%d %H:%M:%S')}] - {content}"
|
199 |
+
content += f"\n\n花费时间:{time.time() - fetch_data.start_time:.2f} 秒"
|
200 |
+
if status_res_json['status'] == 'IN_PROGRESS' and status_res_json.get('imageUrl'):
|
201 |
+
img_url = status_res_json.get('imageUrl')
|
202 |
+
return f"{content}\n[![{fetch_data.task_id}]({img_url})]({img_url})"
|
203 |
+
return content
|
204 |
+
return None
|
205 |
+
|
206 |
+
def handle_file_upload(self, files, chatbot, language):
|
207 |
+
"""
|
208 |
+
handle file upload
|
209 |
+
"""
|
210 |
+
if files:
|
211 |
+
for file in files:
|
212 |
+
if file.name:
|
213 |
+
logging.info(f"尝试读取图像: {file.name}")
|
214 |
+
self.try_read_image(file.name)
|
215 |
+
if self.image_path is not None:
|
216 |
+
chatbot = chatbot + [((self.image_path,), None)]
|
217 |
+
if self.image_bytes is not None:
|
218 |
+
logging.info("使用图片作为输入")
|
219 |
+
return None, chatbot, None
|
220 |
+
|
221 |
+
def reset(self):
|
222 |
+
self.image_bytes = None
|
223 |
+
self.image_path = None
|
224 |
+
return [], "已重置"
|
225 |
+
|
226 |
+
def get_answer_at_once(self):
|
227 |
+
content = self.history[-1]['content']
|
228 |
+
answer = self.get_help()
|
229 |
+
|
230 |
+
if not content.lower().startswith("/mj"):
|
231 |
+
return answer, len(content)
|
232 |
+
|
233 |
+
prompt = content[3:].strip()
|
234 |
+
action = "IMAGINE"
|
235 |
+
first_split_index = prompt.find(self.command_splitter)
|
236 |
+
if first_split_index > 0:
|
237 |
+
action = prompt[:first_split_index]
|
238 |
+
if action not in ["IMAGINE", "DESCRIBE", "UPSCALE",
|
239 |
+
# "VARIATION", "BLEND", "REROLL"
|
240 |
+
]:
|
241 |
+
raise Exception("任务提交失败:未知的任务类型")
|
242 |
+
else:
|
243 |
+
action_index = None
|
244 |
+
action_use_task_id = None
|
245 |
+
if action in ["VARIATION", "UPSCALE", "REROLL"]:
|
246 |
+
action_index = int(prompt[first_split_index + 2:first_split_index + 3])
|
247 |
+
action_use_task_id = prompt[first_split_index + 5:]
|
248 |
+
|
249 |
+
try:
|
250 |
+
res = None
|
251 |
+
if action == "IMAGINE":
|
252 |
+
data = {
|
253 |
+
"prompt": prompt
|
254 |
+
}
|
255 |
+
if self.image_bytes is not None:
|
256 |
+
data["base64"] = 'data:image/png;base64,' + self.image_bytes
|
257 |
+
res = self.request_mj("submit/imagine", "POST",
|
258 |
+
json.dumps(data))
|
259 |
+
elif action == "DESCRIBE":
|
260 |
+
res = self.request_mj("submit/describe", "POST",
|
261 |
+
json.dumps({"base64": 'data:image/png;base64,' + self.image_bytes}))
|
262 |
+
elif action == "BLEND":
|
263 |
+
res = self.request_mj("submit/blend", "POST", json.dumps(
|
264 |
+
{"base64Array": [self.image_bytes, self.image_bytes]}))
|
265 |
+
elif action in ["UPSCALE", "VARIATION", "REROLL"]:
|
266 |
+
res = self.request_mj(
|
267 |
+
"submit/change", "POST",
|
268 |
+
json.dumps({"action": action, "index": action_index, "taskId": action_use_task_id}))
|
269 |
+
res_json = res.json()
|
270 |
+
if not (200 <= res.status_code < 300) or (res_json['code'] not in [1, 22]):
|
271 |
+
answer = "任务提交失败:" + res_json.get('error', res_json.get('description', '未知错误'))
|
272 |
+
else:
|
273 |
+
task_id = res_json['result']
|
274 |
+
prefix_content = f"**画面描述:** {prompt}\n**任务ID:** {task_id}\n"
|
275 |
+
|
276 |
+
fetch_data = Midjourney_Client.FetchDataPack(
|
277 |
+
action=action,
|
278 |
+
prefix_content=prefix_content,
|
279 |
+
task_id=task_id,
|
280 |
+
)
|
281 |
+
fetch_data.prompt = prompt
|
282 |
+
while not fetch_data.finished:
|
283 |
+
answer = self.fetch_status(fetch_data)
|
284 |
+
except Exception as e:
|
285 |
+
logging.error("submit failed", e)
|
286 |
+
answer = "任务提交错误:" + str(e.args[0]) if e.args else '未知错误'
|
287 |
+
|
288 |
+
return answer, tiktoken.get_encoding("cl100k_base").encode(content)
|
289 |
+
|
290 |
+
def get_answer_stream_iter(self):
|
291 |
+
content = self.history[-1]['content']
|
292 |
+
answer = self.get_help()
|
293 |
+
|
294 |
+
if not content.lower().startswith("/mj"):
|
295 |
+
yield answer
|
296 |
+
return
|
297 |
+
|
298 |
+
prompt = content[3:].strip()
|
299 |
+
action = "IMAGINE"
|
300 |
+
first_split_index = prompt.find(self.command_splitter)
|
301 |
+
if first_split_index > 0:
|
302 |
+
action = prompt[:first_split_index]
|
303 |
+
if action not in ["IMAGINE", "DESCRIBE", "UPSCALE",
|
304 |
+
"VARIATION", "BLEND", "REROLL"
|
305 |
+
]:
|
306 |
+
yield "任务提交失败:未知的任务类型"
|
307 |
+
return
|
308 |
+
|
309 |
+
action_index = None
|
310 |
+
action_use_task_id = None
|
311 |
+
if action in ["VARIATION", "UPSCALE", "REROLL"]:
|
312 |
+
action_index = int(prompt[first_split_index + 2:first_split_index + 3])
|
313 |
+
action_use_task_id = prompt[first_split_index + 5:]
|
314 |
+
|
315 |
+
try:
|
316 |
+
res = None
|
317 |
+
if action == "IMAGINE":
|
318 |
+
data = {
|
319 |
+
"prompt": prompt
|
320 |
+
}
|
321 |
+
if self.image_bytes is not None:
|
322 |
+
data["base64"] = 'data:image/png;base64,' + self.image_bytes
|
323 |
+
res = self.request_mj("submit/imagine", "POST",
|
324 |
+
json.dumps(data))
|
325 |
+
elif action == "DESCRIBE":
|
326 |
+
res = self.request_mj("submit/describe", "POST", json.dumps(
|
327 |
+
{"base64": 'data:image/png;base64,' + self.image_bytes}))
|
328 |
+
elif action == "BLEND":
|
329 |
+
res = self.request_mj("submit/blend", "POST", json.dumps(
|
330 |
+
{"base64Array": [self.image_bytes, self.image_bytes]}))
|
331 |
+
elif action in ["UPSCALE", "VARIATION", "REROLL"]:
|
332 |
+
res = self.request_mj(
|
333 |
+
"submit/change", "POST",
|
334 |
+
json.dumps({"action": action, "index": action_index, "taskId": action_use_task_id}))
|
335 |
+
res_json = res.json()
|
336 |
+
if not (200 <= res.status_code < 300) or (res_json['code'] not in [1, 22]):
|
337 |
+
yield "任务提交失败:" + res_json.get('error', res_json.get('description', '未知错误'))
|
338 |
+
else:
|
339 |
+
task_id = res_json['result']
|
340 |
+
prefix_content = f"**画面描述:** {prompt}\n**任务ID:** {task_id}\n"
|
341 |
+
content = f"[{(datetime.now()).strftime('%Y-%m-%d %H:%M:%S')}] - 任务提交成功:" + \
|
342 |
+
res_json.get('description') or '请稍等片刻'
|
343 |
+
yield content
|
344 |
+
|
345 |
+
fetch_data = Midjourney_Client.FetchDataPack(
|
346 |
+
action=action,
|
347 |
+
prefix_content=prefix_content,
|
348 |
+
task_id=task_id,
|
349 |
+
)
|
350 |
+
while not fetch_data.finished:
|
351 |
+
yield self.fetch_status(fetch_data)
|
352 |
+
except Exception as e:
|
353 |
+
logging.error('submit failed', e)
|
354 |
+
yield "任务提交错误:" + str(e.args[0]) if e.args else '未知错误'
|
355 |
+
|
356 |
+
def get_help(self):
|
357 |
+
return """```
|
358 |
+
【绘图帮助】
|
359 |
+
所有命令都需要以 /mj 开头,如:/mj a dog
|
360 |
+
IMAGINE - 绘图,可以省略该命令,后面跟上绘图内容
|
361 |
+
/mj a dog
|
362 |
+
/mj IMAGINE::a cat
|
363 |
+
DESCRIBE - 描述图片,需要在右下角上传需要描述的图片内容
|
364 |
+
/mj DESCRIBE::
|
365 |
+
UPSCALE - 确认后放大图片,第一个数值为需要放大的图片(1~4),第二参数为任务ID
|
366 |
+
/mj UPSCALE::1::123456789
|
367 |
+
请使用SD进行UPSCALE
|
368 |
+
VARIATION - 图片变体,第一个数值为需要放大的图片(1~4),第二参数为任务ID
|
369 |
+
/mj VARIATION::1::123456789
|
370 |
+
|
371 |
+
【绘图参数】
|
372 |
+
所有命令默认会带上参数--v 5.2
|
373 |
+
其他参数参照 https://docs.midjourney.com/docs/parameter-list
|
374 |
+
长宽比 --aspect/--ar
|
375 |
+
--ar 1:2
|
376 |
+
--ar 16:9
|
377 |
+
负面tag --no
|
378 |
+
--no plants
|
379 |
+
--no hands
|
380 |
+
随机种子 --seed
|
381 |
+
--seed 1
|
382 |
+
生成动漫风格(NijiJourney) --niji
|
383 |
+
--niji
|
384 |
+
```
|
385 |
+
"""
|
modules/models/models.py
CHANGED
@@ -24,7 +24,7 @@ from ..presets import *
|
|
24 |
from ..index_func import *
|
25 |
from ..utils import *
|
26 |
from .. import shared
|
27 |
-
from ..config import retrieve_proxy, usage_limit
|
28 |
from modules import config
|
29 |
from .base_model import BaseLLMModel, ModelType
|
30 |
|
@@ -87,21 +87,23 @@ class OpenAIClient(BaseLLMModel):
|
|
87 |
try:
|
88 |
usage_data = self._get_billing_data(usage_url)
|
89 |
except Exception as e:
|
90 |
-
logging.error(f"获取API使用情况失败:" + str(e))
|
|
|
|
|
|
|
|
|
91 |
return i18n("**获取API使用情况失败**")
|
92 |
# rounded_usage = "{:.5f}".format(usage_data["total_usage"] / 100)
|
93 |
rounded_usage = round(usage_data["total_usage"] / 100, 5)
|
94 |
usage_percent = round(usage_data["total_usage"] / usage_limit, 2)
|
|
|
95 |
# return i18n("**本月使用金额** ") + f"\u3000 ${rounded_usage}"
|
96 |
-
return ""
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
</div>
|
103 |
-
<div style="display: flex; justify-content: space-between;"><span>${rounded_usage}</span><span>${usage_limit}</span></div>
|
104 |
-
"""
|
105 |
except requests.exceptions.ConnectTimeout:
|
106 |
status_text = (
|
107 |
STANDARD_ERROR_MSG + CONNECTION_TIMEOUT_MSG + ERROR_RETRIEVE_MSG
|
@@ -161,7 +163,7 @@ class OpenAIClient(BaseLLMModel):
|
|
161 |
|
162 |
# 如果有自定义的api-host,使用自定义host发送请求,否则使用默认设置发送请求
|
163 |
if shared.state.completion_url != COMPLETION_URL:
|
164 |
-
logging.
|
165 |
|
166 |
with retrieve_proxy():
|
167 |
try:
|
@@ -179,9 +181,10 @@ class OpenAIClient(BaseLLMModel):
|
|
179 |
def _refresh_header(self):
|
180 |
self.headers = {
|
181 |
"Content-Type": "application/json",
|
182 |
-
"Authorization": f"Bearer {
|
183 |
}
|
184 |
|
|
|
185 |
def _get_billing_data(self, billing_url):
|
186 |
with retrieve_proxy():
|
187 |
response = requests.get(
|
@@ -206,7 +209,7 @@ class OpenAIClient(BaseLLMModel):
|
|
206 |
chunk_length = len(chunk)
|
207 |
try:
|
208 |
chunk = json.loads(chunk[6:])
|
209 |
-
except
|
210 |
print(i18n("JSON解析错误,收到的内容: ") + f"{chunk}")
|
211 |
error_msg += chunk
|
212 |
continue
|
@@ -560,6 +563,7 @@ def get_model(
|
|
560 |
try:
|
561 |
if model_type == ModelType.OpenAI:
|
562 |
logging.info(f"正在加载OpenAI模型: {model_name}")
|
|
|
563 |
model = OpenAIClient(
|
564 |
model_name=model_name,
|
565 |
api_key=access_key,
|
@@ -610,16 +614,29 @@ def get_model(
|
|
610 |
elif model_type == ModelType.ChuanhuAgent:
|
611 |
from .ChuanhuAgent import ChuanhuAgent_Client
|
612 |
model = ChuanhuAgent_Client(model_name, access_key, user_name=user_name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
613 |
elif model_type == ModelType.Unknown:
|
614 |
raise ValueError(f"未知模型: {model_name}")
|
615 |
logging.info(msg)
|
616 |
except Exception as e:
|
617 |
-
|
|
|
618 |
msg = f"{STANDARD_ERROR_MSG}: {e}"
|
|
|
619 |
if dont_change_lora_selector:
|
620 |
-
return model, msg, chatbot
|
621 |
else:
|
622 |
-
return model, msg, chatbot, gr.Dropdown.update(choices=lora_choices, visible=lora_selector_visibility)
|
623 |
|
624 |
|
625 |
if __name__ == "__main__":
|
|
|
24 |
from ..index_func import *
|
25 |
from ..utils import *
|
26 |
from .. import shared
|
27 |
+
from ..config import retrieve_proxy, usage_limit, sensitive_id
|
28 |
from modules import config
|
29 |
from .base_model import BaseLLMModel, ModelType
|
30 |
|
|
|
87 |
try:
|
88 |
usage_data = self._get_billing_data(usage_url)
|
89 |
except Exception as e:
|
90 |
+
# logging.error(f"获取API使用情况失败: " + str(e))
|
91 |
+
if "Invalid authorization header" in str(e):
|
92 |
+
return i18n("**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id")
|
93 |
+
elif "Incorrect API key provided: sess" in str(e):
|
94 |
+
return i18n("**获取API使用情况失败**,sensitive_id错误或已过期")
|
95 |
return i18n("**获取API使用情况失败**")
|
96 |
# rounded_usage = "{:.5f}".format(usage_data["total_usage"] / 100)
|
97 |
rounded_usage = round(usage_data["total_usage"] / 100, 5)
|
98 |
usage_percent = round(usage_data["total_usage"] / usage_limit, 2)
|
99 |
+
from ..webui import get_html
|
100 |
# return i18n("**本月使用金额** ") + f"\u3000 ${rounded_usage}"
|
101 |
+
return get_html("billing_info.html").format(
|
102 |
+
label = i18n("本月使用金额"),
|
103 |
+
usage_percent = usage_percent,
|
104 |
+
rounded_usage = rounded_usage,
|
105 |
+
usage_limit = usage_limit
|
106 |
+
)
|
|
|
|
|
|
|
107 |
except requests.exceptions.ConnectTimeout:
|
108 |
status_text = (
|
109 |
STANDARD_ERROR_MSG + CONNECTION_TIMEOUT_MSG + ERROR_RETRIEVE_MSG
|
|
|
163 |
|
164 |
# 如果有自定义的api-host,使用自定义host发送请求,否则使用默认设置发送请求
|
165 |
if shared.state.completion_url != COMPLETION_URL:
|
166 |
+
logging.debug(f"使用自定义API URL: {shared.state.completion_url}")
|
167 |
|
168 |
with retrieve_proxy():
|
169 |
try:
|
|
|
181 |
def _refresh_header(self):
|
182 |
self.headers = {
|
183 |
"Content-Type": "application/json",
|
184 |
+
"Authorization": f"Bearer {sensitive_id}",
|
185 |
}
|
186 |
|
187 |
+
|
188 |
def _get_billing_data(self, billing_url):
|
189 |
with retrieve_proxy():
|
190 |
response = requests.get(
|
|
|
209 |
chunk_length = len(chunk)
|
210 |
try:
|
211 |
chunk = json.loads(chunk[6:])
|
212 |
+
except:
|
213 |
print(i18n("JSON解析错误,收到的内容: ") + f"{chunk}")
|
214 |
error_msg += chunk
|
215 |
continue
|
|
|
563 |
try:
|
564 |
if model_type == ModelType.OpenAI:
|
565 |
logging.info(f"正在加载OpenAI模型: {model_name}")
|
566 |
+
access_key = os.environ.get("OPENAI_API_KEY", access_key)
|
567 |
model = OpenAIClient(
|
568 |
model_name=model_name,
|
569 |
api_key=access_key,
|
|
|
614 |
elif model_type == ModelType.ChuanhuAgent:
|
615 |
from .ChuanhuAgent import ChuanhuAgent_Client
|
616 |
model = ChuanhuAgent_Client(model_name, access_key, user_name=user_name)
|
617 |
+
elif model_type == ModelType.GooglePaLM:
|
618 |
+
from .Google_PaLM import Google_PaLM_Client
|
619 |
+
access_key = os.environ.get("GOOGLE_PALM_API_KEY", access_key)
|
620 |
+
model = Google_PaLM_Client(model_name, access_key, user_name=user_name)
|
621 |
+
elif model_type == ModelType.LangchainChat:
|
622 |
+
from .azure import Azure_OpenAI_Client
|
623 |
+
model = Azure_OpenAI_Client(model_name, user_name=user_name)
|
624 |
+
elif model_type == ModelType.Midjourney:
|
625 |
+
from .midjourney import Midjourney_Client
|
626 |
+
mj_proxy_api_secret = os.getenv("MIDJOURNEY_PROXY_API_SECRET")
|
627 |
+
model = Midjourney_Client(model_name, mj_proxy_api_secret, user_name=user_name)
|
628 |
elif model_type == ModelType.Unknown:
|
629 |
raise ValueError(f"未知模型: {model_name}")
|
630 |
logging.info(msg)
|
631 |
except Exception as e:
|
632 |
+
import traceback
|
633 |
+
traceback.print_exc()
|
634 |
msg = f"{STANDARD_ERROR_MSG}: {e}"
|
635 |
+
presudo_key = hide_middle_chars(access_key)
|
636 |
if dont_change_lora_selector:
|
637 |
+
return model, msg, chatbot, gr.update(), access_key, presudo_key
|
638 |
else:
|
639 |
+
return model, msg, chatbot, gr.Dropdown.update(choices=lora_choices, visible=lora_selector_visibility), access_key, presudo_key
|
640 |
|
641 |
|
642 |
if __name__ == "__main__":
|
modules/overwrites.py
CHANGED
@@ -71,23 +71,36 @@ def postprocess_chat_messages(
|
|
71 |
else:
|
72 |
raise ValueError(f"Invalid message for Chatbot component: {chat_message}")
|
73 |
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
else:
|
72 |
raise ValueError(f"Invalid message for Chatbot component: {chat_message}")
|
73 |
|
74 |
+
|
75 |
+
|
76 |
+
def add_classes_to_gradio_component(comp):
|
77 |
+
"""
|
78 |
+
this adds gradio-* to the component for css styling (ie gradio-button to gr.Button), as well as some others
|
79 |
+
code from stable-diffusion-webui <AUTOMATIC1111/stable-diffusion-webui>
|
80 |
+
"""
|
81 |
+
|
82 |
+
comp.elem_classes = [f"gradio-{comp.get_block_name()}", *(comp.elem_classes or [])]
|
83 |
+
|
84 |
+
if getattr(comp, 'multiselect', False):
|
85 |
+
comp.elem_classes.append('multiselect')
|
86 |
+
|
87 |
+
|
88 |
+
def IOComponent_init(self, *args, **kwargs):
|
89 |
+
res = original_IOComponent_init(self, *args, **kwargs)
|
90 |
+
add_classes_to_gradio_component(self)
|
91 |
+
|
92 |
+
return res
|
93 |
+
|
94 |
+
original_IOComponent_init = gr.components.IOComponent.__init__
|
95 |
+
gr.components.IOComponent.__init__ = IOComponent_init
|
96 |
+
|
97 |
+
|
98 |
+
def BlockContext_init(self, *args, **kwargs):
|
99 |
+
res = original_BlockContext_init(self, *args, **kwargs)
|
100 |
+
add_classes_to_gradio_component(self)
|
101 |
+
|
102 |
+
return res
|
103 |
+
|
104 |
+
original_BlockContext_init = gr.blocks.BlockContext.__init__
|
105 |
+
gr.blocks.BlockContext.__init__ = BlockContext_init
|
106 |
+
|
modules/presets.py
CHANGED
@@ -60,19 +60,24 @@ ONLINE_MODELS = [
|
|
60 |
"gpt-4-32k-0613",
|
61 |
"川虎助理",
|
62 |
"川虎助理 Pro",
|
|
|
63 |
"xmchat",
|
|
|
64 |
"yuanai-1.0-base_10B",
|
65 |
"yuanai-1.0-translate",
|
66 |
"yuanai-1.0-dialog",
|
67 |
"yuanai-1.0-rhythm_poems",
|
68 |
"minimax-abab4-chat",
|
69 |
"minimax-abab5-chat",
|
|
|
70 |
]
|
71 |
|
72 |
LOCAL_MODELS = [
|
73 |
"chatglm-6b",
|
74 |
"chatglm-6b-int4",
|
75 |
-
"chatglm-6b-int4-
|
|
|
|
|
76 |
"StableLM",
|
77 |
"MOSS",
|
78 |
"llama-7b-hf",
|
@@ -121,6 +126,7 @@ REPLY_LANGUAGES = [
|
|
121 |
"Español",
|
122 |
"Français",
|
123 |
"Deutsch",
|
|
|
124 |
"跟随问题语言(不稳定)"
|
125 |
]
|
126 |
|
@@ -170,6 +176,8 @@ SUMMARIZE_PROMPT = """Write a concise summary of the following:
|
|
170 |
CONCISE SUMMARY IN 中文:"""
|
171 |
|
172 |
ALREADY_CONVERTED_MARK = "<!-- ALREADY CONVERTED BY PARSER. -->"
|
|
|
|
|
173 |
|
174 |
small_and_beautiful_theme = gr.themes.Soft(
|
175 |
primary_hue=gr.themes.Color(
|
@@ -222,7 +230,7 @@ small_and_beautiful_theme = gr.themes.Soft(
|
|
222 |
# button_primary_background_fill_hover="*primary_400",
|
223 |
# button_primary_border_color="*primary_500",
|
224 |
button_primary_border_color_dark="*primary_600",
|
225 |
-
button_primary_text_color="
|
226 |
button_primary_text_color_dark="white",
|
227 |
button_secondary_background_fill="*neutral_100",
|
228 |
button_secondary_background_fill_hover="*neutral_50",
|
|
|
60 |
"gpt-4-32k-0613",
|
61 |
"川虎助理",
|
62 |
"川虎助理 Pro",
|
63 |
+
"GooglePaLM",
|
64 |
"xmchat",
|
65 |
+
"Azure OpenAI",
|
66 |
"yuanai-1.0-base_10B",
|
67 |
"yuanai-1.0-translate",
|
68 |
"yuanai-1.0-dialog",
|
69 |
"yuanai-1.0-rhythm_poems",
|
70 |
"minimax-abab4-chat",
|
71 |
"minimax-abab5-chat",
|
72 |
+
"midjourney"
|
73 |
]
|
74 |
|
75 |
LOCAL_MODELS = [
|
76 |
"chatglm-6b",
|
77 |
"chatglm-6b-int4",
|
78 |
+
"chatglm-6b-int4-ge",
|
79 |
+
"chatglm2-6b",
|
80 |
+
"chatglm2-6b-int4",
|
81 |
"StableLM",
|
82 |
"MOSS",
|
83 |
"llama-7b-hf",
|
|
|
126 |
"Español",
|
127 |
"Français",
|
128 |
"Deutsch",
|
129 |
+
"한국어",
|
130 |
"跟随问题语言(不稳定)"
|
131 |
]
|
132 |
|
|
|
176 |
CONCISE SUMMARY IN 中文:"""
|
177 |
|
178 |
ALREADY_CONVERTED_MARK = "<!-- ALREADY CONVERTED BY PARSER. -->"
|
179 |
+
START_OF_OUTPUT_MARK = "<!-- SOO IN MESSAGE -->"
|
180 |
+
END_OF_OUTPUT_MARK = "<!-- EOO IN MESSAGE -->"
|
181 |
|
182 |
small_and_beautiful_theme = gr.themes.Soft(
|
183 |
primary_hue=gr.themes.Color(
|
|
|
230 |
# button_primary_background_fill_hover="*primary_400",
|
231 |
# button_primary_border_color="*primary_500",
|
232 |
button_primary_border_color_dark="*primary_600",
|
233 |
+
button_primary_text_color="white",
|
234 |
button_primary_text_color_dark="white",
|
235 |
button_secondary_background_fill="*neutral_100",
|
236 |
button_secondary_background_fill_hover="*neutral_50",
|
modules/repo.py
ADDED
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# -*- coding:utf-8 -*-
|
2 |
+
import os
|
3 |
+
import sys
|
4 |
+
import subprocess
|
5 |
+
from functools import lru_cache
|
6 |
+
import logging
|
7 |
+
import gradio as gr
|
8 |
+
import datetime
|
9 |
+
|
10 |
+
# This file is mainly used to describe repo version info, execute the git command, python pip command, shell command, etc.
|
11 |
+
# Part of the code in this file is referenced from stable-diffusion-webui/modules/launch_utils.py
|
12 |
+
|
13 |
+
python = sys.executable
|
14 |
+
pip = os.environ.get('PIP', "pip")
|
15 |
+
git = os.environ.get('GIT', "git")
|
16 |
+
|
17 |
+
# Pypi index url
|
18 |
+
index_url = os.environ.get('INDEX_URL', "")
|
19 |
+
|
20 |
+
# Whether to default to printing command output
|
21 |
+
default_command_live = True
|
22 |
+
|
23 |
+
|
24 |
+
def run(command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live) -> str:
|
25 |
+
if desc is not None:
|
26 |
+
print(desc)
|
27 |
+
run_kwargs = {
|
28 |
+
"args": command,
|
29 |
+
"shell": True,
|
30 |
+
"env": os.environ if custom_env is None else custom_env,
|
31 |
+
"encoding": 'utf8',
|
32 |
+
"errors": 'ignore',
|
33 |
+
}
|
34 |
+
|
35 |
+
if not live:
|
36 |
+
run_kwargs["stdout"] = run_kwargs["stderr"] = subprocess.PIPE
|
37 |
+
|
38 |
+
result = subprocess.run(**run_kwargs)
|
39 |
+
if result.returncode != 0:
|
40 |
+
error_bits = [
|
41 |
+
f"{errdesc or 'Error running command'}.",
|
42 |
+
f"Command: {command}",
|
43 |
+
f"Error code: {result.returncode}",
|
44 |
+
]
|
45 |
+
if result.stdout:
|
46 |
+
error_bits.append(f"stdout: {result.stdout}")
|
47 |
+
if result.stderr:
|
48 |
+
error_bits.append(f"stderr: {result.stderr}")
|
49 |
+
raise RuntimeError("\n".join(error_bits))
|
50 |
+
|
51 |
+
return (result.stdout or "")
|
52 |
+
|
53 |
+
|
54 |
+
def run_pip(command, desc=None, pref=None, live=default_command_live):
|
55 |
+
# if args.skip_install:
|
56 |
+
# return
|
57 |
+
|
58 |
+
index_url_line = f' --index-url {index_url}' if index_url != '' else ''
|
59 |
+
return run(
|
60 |
+
f'"{python}" -m pip {command} --prefer-binary{index_url_line}',
|
61 |
+
desc=f"{pref} Installing {desc}...",
|
62 |
+
errdesc=f"Couldn't install {desc}",
|
63 |
+
live=live
|
64 |
+
)
|
65 |
+
|
66 |
+
|
67 |
+
@lru_cache()
|
68 |
+
def commit_hash():
|
69 |
+
try:
|
70 |
+
return subprocess.check_output([git, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip()
|
71 |
+
except Exception:
|
72 |
+
return "<none>"
|
73 |
+
|
74 |
+
def commit_html():
|
75 |
+
commit = commit_hash()
|
76 |
+
if commit != "<none>":
|
77 |
+
short_commit = commit[0:7]
|
78 |
+
commit_info = f'<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/commit/{short_commit}">{short_commit}</a>'
|
79 |
+
else:
|
80 |
+
commit_info = "unknown \U0001F615"
|
81 |
+
return commit_info
|
82 |
+
|
83 |
+
@lru_cache()
|
84 |
+
def tag_html():
|
85 |
+
try:
|
86 |
+
latest_tag = run(f"{git} describe --tags --abbrev=0", live=False).strip()
|
87 |
+
try:
|
88 |
+
# tag = subprocess.check_output([git, "describe", "--tags", "--exact-match"], shell=False, encoding='utf8').strip()
|
89 |
+
tag = run(f"{git} describe --tags --exact-match", live=False).strip()
|
90 |
+
except Exception:
|
91 |
+
tag = "<edited>"
|
92 |
+
except Exception:
|
93 |
+
tag = "<none>"
|
94 |
+
|
95 |
+
if tag == "<none>":
|
96 |
+
tag_info = "unknown \U0001F615"
|
97 |
+
elif tag == "<edited>":
|
98 |
+
tag_info = f'<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/releases/tag/{latest_tag}">{latest_tag}</a><span style="font-size:smaller">*</span>'
|
99 |
+
else:
|
100 |
+
tag_info = f'<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/releases/tag/{tag}">{tag}</a>'
|
101 |
+
|
102 |
+
return tag_info
|
103 |
+
|
104 |
+
def repo_tag_html():
|
105 |
+
commit_version = commit_html()
|
106 |
+
tag_version = tag_html()
|
107 |
+
return tag_version if tag_version != "unknown \U0001F615" else commit_version
|
108 |
+
|
109 |
+
def versions_html():
|
110 |
+
python_version = ".".join([str(x) for x in sys.version_info[0:3]])
|
111 |
+
repo_version = repo_tag_html()
|
112 |
+
return f"""
|
113 |
+
Python: <span title="{sys.version}">{python_version}</span>
|
114 |
+
•
|
115 |
+
Gradio: {gr.__version__}
|
116 |
+
•
|
117 |
+
<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT">ChuanhuChat</a>: {repo_version}
|
118 |
+
"""
|
119 |
+
|
120 |
+
def version_time():
|
121 |
+
try:
|
122 |
+
commit_time = subprocess.check_output(f"TZ=UTC {git} log -1 --format=%cd --date='format-local:%Y-%m-%dT%H:%M:%SZ'", shell=True, encoding='utf8').strip()
|
123 |
+
# commit_time = run(f"TZ=UTC {git} log -1 --format=%cd --date='format-local:%Y-%m-%dT%H:%M:%SZ'").strip()
|
124 |
+
except Exception:
|
125 |
+
commit_time = "unknown"
|
126 |
+
return commit_time
|
127 |
+
|
128 |
+
|
129 |
+
|
130 |
+
def get_current_branch():
|
131 |
+
try:
|
132 |
+
# branch = run(f"{git} rev-parse --abbrev-ref HEAD").strip()
|
133 |
+
branch = subprocess.check_output([git, "rev-parse", "--abbrev-ref", "HEAD"], shell=False, encoding='utf8').strip()
|
134 |
+
except Exception:
|
135 |
+
branch = "<none>"
|
136 |
+
return branch
|
137 |
+
|
138 |
+
|
139 |
+
def get_latest_release():
|
140 |
+
try:
|
141 |
+
import requests
|
142 |
+
release = requests.get("https://api.github.com/repos/GaiZhenbiao/ChuanhuChatGPT/releases/latest").json()
|
143 |
+
tag = release["tag_name"]
|
144 |
+
release_note = release["body"]
|
145 |
+
need_pip = release_note.find("requirements reinstall needed") != -1
|
146 |
+
except Exception:
|
147 |
+
tag = "<none>"
|
148 |
+
release_note = ""
|
149 |
+
need_pip = False
|
150 |
+
return {"tag": tag, "release_note": release_note, "need_pip": need_pip}
|
151 |
+
|
152 |
+
def get_tag_commit_hash(tag):
|
153 |
+
try:
|
154 |
+
import requests
|
155 |
+
tags = requests.get("https://api.github.com/repos/GaiZhenbiao/ChuanhuChatGPT/tags").json()
|
156 |
+
commit_hash = [x["commit"]["sha"] for x in tags if x["name"] == tag][0]
|
157 |
+
except Exception:
|
158 |
+
commit_hash = "<none>"
|
159 |
+
return commit_hash
|
160 |
+
|
161 |
+
def repo_need_stash():
|
162 |
+
try:
|
163 |
+
return subprocess.check_output([git, "diff-index", "--quiet", "HEAD", "--"], shell=False, encoding='utf8').strip() != ""
|
164 |
+
except Exception:
|
165 |
+
return True
|
166 |
+
|
167 |
+
def background_update():
|
168 |
+
# {git} fetch --all && ({git} pull https://github.com/GaiZhenbiao/ChuanhuChatGPT.git main -f || ({git} stash && {git} pull https://github.com/GaiZhenbiao/ChuanhuChatGPT.git main -f && {git} stash pop)) && {pip} install -r requirements.txt")
|
169 |
+
try:
|
170 |
+
latest_release = get_latest_release()
|
171 |
+
latest_release_tag = latest_release["tag"]
|
172 |
+
latest_release_hash = get_tag_commit_hash(latest_release_tag)
|
173 |
+
need_pip = latest_release["need_pip"]
|
174 |
+
need_stash = repo_need_stash()
|
175 |
+
|
176 |
+
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
177 |
+
current_branch = get_current_branch()
|
178 |
+
updater_branch = f'tmp_{timestamp}'
|
179 |
+
backup_branch = f'backup_{timestamp}'
|
180 |
+
track_repo = "https://github.com/GaiZhenbiao/ChuanhuChatGPT.git"
|
181 |
+
try:
|
182 |
+
try:
|
183 |
+
run(f"{git} fetch {track_repo}", desc="[Updater] Fetching from github...", live=False)
|
184 |
+
except Exception:
|
185 |
+
logging.error(f"Update failed in fetching, check your network connection")
|
186 |
+
return "failed"
|
187 |
+
|
188 |
+
run(f'{git} stash push --include-untracked -m "updater-{timestamp}"',
|
189 |
+
desc=f'[Updater] Restoring you local changes on stash updater-{timestamp}', live=False) if need_stash else None
|
190 |
+
|
191 |
+
run(f"{git} checkout -b {backup_branch}", live=False)
|
192 |
+
run(f"{git} checkout -b {updater_branch}", live=False)
|
193 |
+
run(f"{git} reset --hard FETCH_HEAD", live=False)
|
194 |
+
run(f"{git} reset --hard {latest_release_hash}", desc=f'[Updater] Checking out {latest_release_tag}...', live=False)
|
195 |
+
run(f"{git} checkout {current_branch}", live=False)
|
196 |
+
|
197 |
+
try:
|
198 |
+
run(f"{git} merge --no-edit {updater_branch} -q", desc=f"[Updater] Trying to apply latest update on version {latest_release_tag}...")
|
199 |
+
except Exception:
|
200 |
+
logging.error(f"Update failed in merging")
|
201 |
+
try:
|
202 |
+
run(f"{git} merge --abort", desc="[Updater] Conflict detected, canceling update...")
|
203 |
+
run(f"{git} reset --hard {backup_branch}", live=False)
|
204 |
+
run(f"{git} branch -D -f {updater_branch}", live=False)
|
205 |
+
run(f"{git} branch -D -f {backup_branch}", live=False)
|
206 |
+
run(f"{git} stash pop", live=False) if need_stash else None
|
207 |
+
logging.error(f"Update failed, but your file was safely reset to the state before the update.")
|
208 |
+
return "failed"
|
209 |
+
except Exception as e:
|
210 |
+
logging.error(f"!!!Update failed in resetting, try to reset your files manually. {e}")
|
211 |
+
return "failed"
|
212 |
+
|
213 |
+
if need_stash:
|
214 |
+
try:
|
215 |
+
run(f"{git} stash apply", desc="[Updater] Trying to restore your local modifications...", live=False)
|
216 |
+
except Exception:
|
217 |
+
run(f"{git} reset --hard {backup_branch}", desc="[Updater] Conflict detected, canceling update...", live=False)
|
218 |
+
run(f"{git} branch -D -f {updater_branch}", live=False)
|
219 |
+
run(f"{git} branch -D -f {backup_branch}", live=False)
|
220 |
+
run(f"{git} stash pop", live=False)
|
221 |
+
logging.error(f"Update failed in applying your local changes, but your file was safely reset to the state before the update.")
|
222 |
+
return "failed"
|
223 |
+
run(f"{git} stash drop", live=False)
|
224 |
+
|
225 |
+
run(f"{git} branch -D -f {updater_branch}", live=False)
|
226 |
+
run(f"{git} branch -D -f {backup_branch}", live=False)
|
227 |
+
except Exception as e:
|
228 |
+
logging.error(f"Update failed: {e}")
|
229 |
+
return "failed"
|
230 |
+
if need_pip:
|
231 |
+
try:
|
232 |
+
run_pip(f"install -r requirements.txt", pref="[Updater]", desc="requirements", live=False)
|
233 |
+
except Exception:
|
234 |
+
logging.error(f"Update failed in pip install")
|
235 |
+
return "failed"
|
236 |
+
return "success"
|
237 |
+
except Exception as e:
|
238 |
+
logging.error(f"Update failed: {e}")
|
239 |
+
return "failed"
|
modules/shared.py
CHANGED
@@ -62,3 +62,4 @@ state = State()
|
|
62 |
|
63 |
modules_path = os.path.dirname(os.path.realpath(__file__))
|
64 |
chuanhu_path = os.path.dirname(modules_path)
|
|
|
|
62 |
|
63 |
modules_path = os.path.dirname(os.path.realpath(__file__))
|
64 |
chuanhu_path = os.path.dirname(modules_path)
|
65 |
+
assets_path = os.path.join(chuanhu_path, "web_assets")
|
modules/train_func.py
ADDED
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import logging
|
3 |
+
import traceback
|
4 |
+
|
5 |
+
import openai
|
6 |
+
import gradio as gr
|
7 |
+
import ujson as json
|
8 |
+
import commentjson
|
9 |
+
import openpyxl
|
10 |
+
|
11 |
+
import modules.presets as presets
|
12 |
+
from modules.utils import get_file_hash, count_token
|
13 |
+
from modules.presets import i18n
|
14 |
+
|
15 |
+
def excel_to_jsonl(filepath, preview=False):
|
16 |
+
# 打开Excel文件
|
17 |
+
workbook = openpyxl.load_workbook(filepath)
|
18 |
+
|
19 |
+
# 获取第一个工作表
|
20 |
+
sheet = workbook.active
|
21 |
+
|
22 |
+
# 获取所有行数据
|
23 |
+
data = []
|
24 |
+
for row in sheet.iter_rows(values_only=True):
|
25 |
+
data.append(row)
|
26 |
+
|
27 |
+
# 构建字典列表
|
28 |
+
headers = data[0]
|
29 |
+
jsonl = []
|
30 |
+
for row in data[1:]:
|
31 |
+
row_data = dict(zip(headers, row))
|
32 |
+
if any(row_data.values()):
|
33 |
+
jsonl.append(row_data)
|
34 |
+
formatted_jsonl = []
|
35 |
+
for i in jsonl:
|
36 |
+
if "提问" in i and "答案" in i:
|
37 |
+
if "系统" in i :
|
38 |
+
formatted_jsonl.append({
|
39 |
+
"messages":[
|
40 |
+
{"role": "system", "content": i["系统"]},
|
41 |
+
{"role": "user", "content": i["提问"]},
|
42 |
+
{"role": "assistant", "content": i["答案"]}
|
43 |
+
]
|
44 |
+
})
|
45 |
+
else:
|
46 |
+
formatted_jsonl.append({
|
47 |
+
"messages":[
|
48 |
+
{"role": "user", "content": i["提问"]},
|
49 |
+
{"role": "assistant", "content": i["答案"]}
|
50 |
+
]
|
51 |
+
})
|
52 |
+
else:
|
53 |
+
logging.warning(f"跳过一行数据,因为没有找到提问和答案: {i}")
|
54 |
+
return formatted_jsonl
|
55 |
+
|
56 |
+
def jsonl_save_to_disk(jsonl, filepath):
|
57 |
+
file_hash = get_file_hash(file_paths = [filepath])
|
58 |
+
os.makedirs("files", exist_ok=True)
|
59 |
+
save_path = f"files/{file_hash}.jsonl"
|
60 |
+
with open(save_path, "w") as f:
|
61 |
+
f.write("\n".join([json.dumps(i, ensure_ascii=False) for i in jsonl]))
|
62 |
+
return save_path
|
63 |
+
|
64 |
+
def estimate_cost(ds):
|
65 |
+
dialogues = []
|
66 |
+
for l in ds:
|
67 |
+
for m in l["messages"]:
|
68 |
+
dialogues.append(m["content"])
|
69 |
+
dialogues = "\n".join(dialogues)
|
70 |
+
tokens = count_token(dialogues)
|
71 |
+
return f"Token 数约为 {tokens},预估每轮(epoch)费用约为 {tokens / 1000 * 0.008} 美元。"
|
72 |
+
|
73 |
+
|
74 |
+
def handle_dataset_selection(file_src):
|
75 |
+
logging.info(f"Loading dataset {file_src.name}...")
|
76 |
+
preview = ""
|
77 |
+
if file_src.name.endswith(".jsonl"):
|
78 |
+
with open(file_src.name, "r") as f:
|
79 |
+
ds = [json.loads(l) for l in f.readlines()]
|
80 |
+
else:
|
81 |
+
ds = excel_to_jsonl(file_src.name)
|
82 |
+
preview = ds[0]
|
83 |
+
|
84 |
+
return preview, gr.update(interactive=True), estimate_cost(ds)
|
85 |
+
|
86 |
+
def upload_to_openai(file_src):
|
87 |
+
openai.api_key = os.getenv("OPENAI_API_KEY")
|
88 |
+
dspath = file_src.name
|
89 |
+
msg = ""
|
90 |
+
logging.info(f"Uploading dataset {dspath}...")
|
91 |
+
if dspath.endswith(".xlsx"):
|
92 |
+
jsonl = excel_to_jsonl(dspath)
|
93 |
+
dspath = jsonl_save_to_disk(jsonl, dspath)
|
94 |
+
try:
|
95 |
+
uploaded = openai.File.create(
|
96 |
+
file=open(dspath, "rb"),
|
97 |
+
purpose='fine-tune'
|
98 |
+
)
|
99 |
+
return uploaded.id, f"上传成功"
|
100 |
+
except Exception as e:
|
101 |
+
traceback.print_exc()
|
102 |
+
return "", f"上传失败,原因:{ e }"
|
103 |
+
|
104 |
+
def build_event_description(id, status, trained_tokens, name=i18n("暂时未知")):
|
105 |
+
# convert to markdown
|
106 |
+
return f"""
|
107 |
+
#### 训练任务 {id}
|
108 |
+
|
109 |
+
模型名称:{name}
|
110 |
+
|
111 |
+
状态:{status}
|
112 |
+
|
113 |
+
已经训练了 {trained_tokens} 个token
|
114 |
+
"""
|
115 |
+
|
116 |
+
def start_training(file_id, suffix, epochs):
|
117 |
+
openai.api_key = os.getenv("OPENAI_API_KEY")
|
118 |
+
try:
|
119 |
+
job = openai.FineTuningJob.create(training_file=file_id, model="gpt-3.5-turbo", suffix=suffix, hyperparameters={"n_epochs": epochs})
|
120 |
+
return build_event_description(job.id, job.status, job.trained_tokens)
|
121 |
+
except Exception as e:
|
122 |
+
traceback.print_exc()
|
123 |
+
if "is not ready" in str(e):
|
124 |
+
return "训练出错,因为文件还没准备好。OpenAI 需要一点时间准备文件,过几分钟再来试试。"
|
125 |
+
return f"训练失败,原因:{ e }"
|
126 |
+
|
127 |
+
def get_training_status():
|
128 |
+
openai.api_key = os.getenv("OPENAI_API_KEY")
|
129 |
+
active_jobs = [build_event_description(job["id"], job["status"], job["trained_tokens"], job["fine_tuned_model"]) for job in openai.FineTuningJob.list(limit=10)["data"] if job["status"] != "cancelled"]
|
130 |
+
return "\n\n".join(active_jobs), gr.update(interactive=True) if len(active_jobs) > 0 else gr.update(interactive=False)
|
131 |
+
|
132 |
+
def handle_dataset_clear():
|
133 |
+
return gr.update(value=None), gr.update(interactive=False)
|
134 |
+
|
135 |
+
def add_to_models():
|
136 |
+
openai.api_key = os.getenv("OPENAI_API_KEY")
|
137 |
+
succeeded_jobs = [job for job in openai.FineTuningJob.list()["data"] if job["status"] == "succeeded"]
|
138 |
+
extra_models = [job["fine_tuned_model"] for job in succeeded_jobs]
|
139 |
+
for i in extra_models:
|
140 |
+
if i not in presets.MODELS:
|
141 |
+
presets.MODELS.append(i)
|
142 |
+
|
143 |
+
with open('config.json', 'r') as f:
|
144 |
+
data = commentjson.load(f)
|
145 |
+
if 'extra_models' in data:
|
146 |
+
for i in extra_models:
|
147 |
+
if i not in data['extra_models']:
|
148 |
+
data['extra_models'].append(i)
|
149 |
+
else:
|
150 |
+
data['extra_models'] = extra_models
|
151 |
+
with open('config.json', 'w') as f:
|
152 |
+
commentjson.dump(data, f, indent=4)
|
153 |
+
|
154 |
+
return gr.update(choices=presets.MODELS), f"成功添加了 {len(succeeded_jobs)} 个模型。"
|
155 |
+
|
156 |
+
def cancel_all_jobs():
|
157 |
+
openai.api_key = os.getenv("OPENAI_API_KEY")
|
158 |
+
jobs = [job for job in openai.FineTuningJob.list()["data"] if job["status"] not in ["cancelled", "succeeded"]]
|
159 |
+
for job in jobs:
|
160 |
+
openai.FineTuningJob.cancel(job["id"])
|
161 |
+
return f"成功取消了 {len(jobs)} 个训练任务。"
|
modules/utils.py
CHANGED
@@ -2,16 +2,14 @@
|
|
2 |
from __future__ import annotations
|
3 |
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Tuple, Type
|
4 |
import logging
|
5 |
-
import json
|
6 |
import os
|
7 |
import datetime
|
8 |
-
import hashlib
|
9 |
import csv
|
10 |
import requests
|
11 |
import re
|
12 |
import html
|
13 |
-
import
|
14 |
-
import subprocess
|
15 |
|
16 |
import gradio as gr
|
17 |
from pypinyin import lazy_pinyin
|
@@ -47,6 +45,9 @@ def set_key(current_model, *args):
|
|
47 |
def load_chat_history(current_model, *args):
|
48 |
return current_model.load_chat_history(*args)
|
49 |
|
|
|
|
|
|
|
50 |
def interrupt(current_model, *args):
|
51 |
return current_model.interrupt(*args)
|
52 |
|
@@ -125,9 +126,10 @@ def dislike(current_model, *args):
|
|
125 |
return current_model.dislike(*args)
|
126 |
|
127 |
|
128 |
-
def count_token(
|
129 |
encoding = tiktoken.get_encoding("cl100k_base")
|
130 |
-
input_str
|
|
|
131 |
length = len(encoding.encode(input_str))
|
132 |
return length
|
133 |
|
@@ -202,6 +204,28 @@ def convert_mdtext(md_text): # deprecated
|
|
202 |
output += ALREADY_CONVERTED_MARK
|
203 |
return output
|
204 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
def convert_bot_before_marked(chat_message):
|
206 |
"""
|
207 |
注意不能给输出加缩进, 否则会被marked解析成代码块
|
@@ -209,12 +233,13 @@ def convert_bot_before_marked(chat_message):
|
|
209 |
if '<div class="md-message">' in chat_message:
|
210 |
return chat_message
|
211 |
else:
|
|
|
|
|
|
|
212 |
code_block_pattern = re.compile(r"```(.*?)(?:```|$)", re.DOTALL)
|
213 |
code_blocks = code_block_pattern.findall(chat_message)
|
214 |
non_code_parts = code_block_pattern.split(chat_message)[::2]
|
215 |
result = []
|
216 |
-
|
217 |
-
raw = f'<div class="raw-message hideM">{escape_markdown(chat_message)}</div>'
|
218 |
for non_code, code in zip(non_code_parts, code_blocks + [""]):
|
219 |
if non_code.strip():
|
220 |
result.append(non_code)
|
@@ -222,7 +247,7 @@ def convert_bot_before_marked(chat_message):
|
|
222 |
code = f"\n```{code}\n```"
|
223 |
result.append(code)
|
224 |
result = "".join(result)
|
225 |
-
md = f'<div class="md-message"
|
226 |
return raw + md
|
227 |
|
228 |
def convert_user_before_marked(chat_message):
|
@@ -236,7 +261,7 @@ def escape_markdown(text):
|
|
236 |
Escape Markdown special characters to HTML-safe equivalents.
|
237 |
"""
|
238 |
escape_chars = {
|
239 |
-
' ': ' ',
|
240 |
'_': '_',
|
241 |
'*': '*',
|
242 |
'[': '[',
|
@@ -253,8 +278,12 @@ def escape_markdown(text):
|
|
253 |
'`': '`',
|
254 |
'>': '>',
|
255 |
'<': '<',
|
256 |
-
'|': '|'
|
|
|
|
|
|
|
257 |
}
|
|
|
258 |
return ''.join(escape_chars.get(c, c) for c in text)
|
259 |
|
260 |
|
@@ -508,55 +537,19 @@ def transfer_input(inputs):
|
|
508 |
)
|
509 |
|
510 |
|
|
|
|
|
511 |
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
raise RuntimeError(f"""{errdesc or 'Error running command'}.
|
519 |
-
Command: {command}
|
520 |
-
Error code: {result.returncode}""")
|
521 |
-
|
522 |
-
return ""
|
523 |
-
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, env=os.environ if custom_env is None else custom_env)
|
524 |
-
if result.returncode != 0:
|
525 |
-
message = f"""{errdesc or 'Error running command'}.
|
526 |
-
Command: {command}
|
527 |
-
Error code: {result.returncode}
|
528 |
-
stdout: {result.stdout.decode(encoding="utf8", errors="ignore") if len(result.stdout)>0 else '<empty>'}
|
529 |
-
stderr: {result.stderr.decode(encoding="utf8", errors="ignore") if len(result.stderr)>0 else '<empty>'}
|
530 |
-
"""
|
531 |
-
raise RuntimeError(message)
|
532 |
-
return result.stdout.decode(encoding="utf8", errors="ignore")
|
533 |
-
|
534 |
-
def versions_html():
|
535 |
-
git = os.environ.get('GIT', "git")
|
536 |
-
python_version = ".".join([str(x) for x in sys.version_info[0:3]])
|
537 |
-
try:
|
538 |
-
commit_hash = run(f"{git} rev-parse HEAD").strip()
|
539 |
-
except Exception:
|
540 |
-
commit_hash = "<none>"
|
541 |
-
if commit_hash != "<none>":
|
542 |
-
short_commit = commit_hash[0:7]
|
543 |
-
commit_info = f"<a style=\"text-decoration:none;color:inherit\" href=\"https://github.com/GaiZhenbiao/ChuanhuChatGPT/commit/{short_commit}\">{short_commit}</a>"
|
544 |
else:
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
•
|
549 |
-
Gradio: {gr.__version__}
|
550 |
-
•
|
551 |
-
<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT">ChuanhuChat</a>: {commit_info}
|
552 |
-
"""
|
553 |
-
|
554 |
-
def get_html(filename):
|
555 |
-
path = os.path.join(shared.chuanhu_path, "assets", "html", filename)
|
556 |
-
if os.path.exists(path):
|
557 |
-
with open(path, encoding="utf8") as file:
|
558 |
-
return file.read()
|
559 |
-
return ""
|
560 |
|
561 |
def add_source_numbers(lst, source_name = "Source", use_source = True):
|
562 |
if use_source:
|
@@ -654,3 +647,37 @@ def get_history_filepath(username):
|
|
654 |
|
655 |
latest_file = os.path.join(dirname, latest_file)
|
656 |
return latest_file
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
from __future__ import annotations
|
3 |
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Tuple, Type
|
4 |
import logging
|
5 |
+
import commentjson as json
|
6 |
import os
|
7 |
import datetime
|
|
|
8 |
import csv
|
9 |
import requests
|
10 |
import re
|
11 |
import html
|
12 |
+
import hashlib
|
|
|
13 |
|
14 |
import gradio as gr
|
15 |
from pypinyin import lazy_pinyin
|
|
|
45 |
def load_chat_history(current_model, *args):
|
46 |
return current_model.load_chat_history(*args)
|
47 |
|
48 |
+
def delete_chat_history(current_model, *args):
|
49 |
+
return current_model.delete_chat_history(*args)
|
50 |
+
|
51 |
def interrupt(current_model, *args):
|
52 |
return current_model.interrupt(*args)
|
53 |
|
|
|
126 |
return current_model.dislike(*args)
|
127 |
|
128 |
|
129 |
+
def count_token(input_str):
|
130 |
encoding = tiktoken.get_encoding("cl100k_base")
|
131 |
+
if type(input_str) == dict:
|
132 |
+
input_str = f"role: {input_str['role']}, content: {input_str['content']}"
|
133 |
length = len(encoding.encode(input_str))
|
134 |
return length
|
135 |
|
|
|
204 |
output += ALREADY_CONVERTED_MARK
|
205 |
return output
|
206 |
|
207 |
+
|
208 |
+
def clip_rawtext(chat_message, need_escape=True):
|
209 |
+
# first, clip hr line
|
210 |
+
hr_pattern = r'\n\n<hr class="append-display no-in-raw" />(.*?)'
|
211 |
+
hr_match = re.search(hr_pattern, chat_message, re.DOTALL)
|
212 |
+
message_clipped = chat_message[:hr_match.start()] if hr_match else chat_message
|
213 |
+
# second, avoid agent-prefix being escaped
|
214 |
+
agent_prefix_pattern = r'<!-- S O PREFIX --><p class="agent-prefix">(.*?)<\/p><!-- E O PREFIX -->'
|
215 |
+
agent_matches = re.findall(agent_prefix_pattern, message_clipped)
|
216 |
+
final_message = ""
|
217 |
+
if agent_matches:
|
218 |
+
agent_parts = re.split(agent_prefix_pattern, message_clipped)
|
219 |
+
for i, part in enumerate(agent_parts):
|
220 |
+
if i % 2 == 0:
|
221 |
+
final_message += escape_markdown(part) if need_escape else part
|
222 |
+
else:
|
223 |
+
final_message += f'<!-- S O PREFIX --><p class="agent-prefix">{part}</p><!-- E O PREFIX -->'
|
224 |
+
else:
|
225 |
+
final_message = escape_markdown(message_clipped) if need_escape else message_clipped
|
226 |
+
return final_message
|
227 |
+
|
228 |
+
|
229 |
def convert_bot_before_marked(chat_message):
|
230 |
"""
|
231 |
注意不能给输出加缩进, 否则会被marked解析成代码块
|
|
|
233 |
if '<div class="md-message">' in chat_message:
|
234 |
return chat_message
|
235 |
else:
|
236 |
+
raw = f'<div class="raw-message hideM"><pre>{clip_rawtext(chat_message)}</pre></div>'
|
237 |
+
# really_raw = f'{START_OF_OUTPUT_MARK}<div class="really-raw hideM">{clip_rawtext(chat_message, need_escape=False)}\n</div>{END_OF_OUTPUT_MARK}'
|
238 |
+
|
239 |
code_block_pattern = re.compile(r"```(.*?)(?:```|$)", re.DOTALL)
|
240 |
code_blocks = code_block_pattern.findall(chat_message)
|
241 |
non_code_parts = code_block_pattern.split(chat_message)[::2]
|
242 |
result = []
|
|
|
|
|
243 |
for non_code, code in zip(non_code_parts, code_blocks + [""]):
|
244 |
if non_code.strip():
|
245 |
result.append(non_code)
|
|
|
247 |
code = f"\n```{code}\n```"
|
248 |
result.append(code)
|
249 |
result = "".join(result)
|
250 |
+
md = f'<div class="md-message">\n\n{result}\n</div>'
|
251 |
return raw + md
|
252 |
|
253 |
def convert_user_before_marked(chat_message):
|
|
|
261 |
Escape Markdown special characters to HTML-safe equivalents.
|
262 |
"""
|
263 |
escape_chars = {
|
264 |
+
# ' ': ' ',
|
265 |
'_': '_',
|
266 |
'*': '*',
|
267 |
'[': '[',
|
|
|
278 |
'`': '`',
|
279 |
'>': '>',
|
280 |
'<': '<',
|
281 |
+
'|': '|',
|
282 |
+
'$': '$',
|
283 |
+
':': ':',
|
284 |
+
'\n': '<br>',
|
285 |
}
|
286 |
+
text = text.replace(' ', ' ')
|
287 |
return ''.join(escape_chars.get(c, c) for c in text)
|
288 |
|
289 |
|
|
|
537 |
)
|
538 |
|
539 |
|
540 |
+
def update_chuanhu():
|
541 |
+
from .repo import background_update
|
542 |
|
543 |
+
print("[Updater] Trying to update...")
|
544 |
+
update_status = background_update()
|
545 |
+
if update_status == "success":
|
546 |
+
logging.info("Successfully updated, restart needed")
|
547 |
+
status = '<span id="update-status" class="hideK">success</span>'
|
548 |
+
return gr.Markdown.update(value=i18n("更新成功,请重启本程序")+status)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
549 |
else:
|
550 |
+
status = '<span id="update-status" class="hideK">failure</span>'
|
551 |
+
return gr.Markdown.update(value=i18n("更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)")+status)
|
552 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
553 |
|
554 |
def add_source_numbers(lst, source_name = "Source", use_source = True):
|
555 |
if use_source:
|
|
|
647 |
|
648 |
latest_file = os.path.join(dirname, latest_file)
|
649 |
return latest_file
|
650 |
+
|
651 |
+
def beautify_err_msg(err_msg):
|
652 |
+
if "insufficient_quota" in err_msg:
|
653 |
+
return i18n("剩余配额不足,[进一步了解](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98#you-exceeded-your-current-quota-please-check-your-plan-and-billing-details)")
|
654 |
+
if "The model: gpt-4 does not exist" in err_msg:
|
655 |
+
return i18n("你没有权限访问 GPT4,[进一步了解](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)")
|
656 |
+
if "Resource not found" in err_msg:
|
657 |
+
return i18n("请查看 config_example.json,配置 Azure OpenAI")
|
658 |
+
return err_msg
|
659 |
+
|
660 |
+
def auth_from_conf(username, password):
|
661 |
+
try:
|
662 |
+
with open("config.json", encoding="utf-8") as f:
|
663 |
+
conf = json.load(f)
|
664 |
+
usernames, passwords = [i[0] for i in conf["users"]], [i[1] for i in conf["users"]]
|
665 |
+
if username in usernames:
|
666 |
+
if passwords[usernames.index(username)] == password:
|
667 |
+
return True
|
668 |
+
return False
|
669 |
+
except:
|
670 |
+
return False
|
671 |
+
|
672 |
+
def get_file_hash(file_src=None, file_paths=None):
|
673 |
+
if file_src:
|
674 |
+
file_paths = [x.name for x in file_src]
|
675 |
+
file_paths.sort(key=lambda x: os.path.basename(x))
|
676 |
+
|
677 |
+
md5_hash = hashlib.md5()
|
678 |
+
for file_path in file_paths:
|
679 |
+
with open(file_path, "rb") as f:
|
680 |
+
while chunk := f.read(8192):
|
681 |
+
md5_hash.update(chunk)
|
682 |
+
|
683 |
+
return md5_hash.hexdigest()
|
modules/webui.py
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
from collections import namedtuple
|
3 |
+
import os
|
4 |
+
import gradio as gr
|
5 |
+
|
6 |
+
from . import shared
|
7 |
+
|
8 |
+
# with open("./assets/ChuanhuChat.js", "r", encoding="utf-8") as f, \
|
9 |
+
# open("./assets/external-scripts.js", "r", encoding="utf-8") as f1:
|
10 |
+
# customJS = f.read()
|
11 |
+
# externalScripts = f1.read()
|
12 |
+
|
13 |
+
|
14 |
+
def get_html(filename):
|
15 |
+
path = os.path.join(shared.chuanhu_path, "web_assets", "html", filename)
|
16 |
+
if os.path.exists(path):
|
17 |
+
with open(path, encoding="utf8") as file:
|
18 |
+
return file.read()
|
19 |
+
return ""
|
20 |
+
|
21 |
+
def webpath(fn):
|
22 |
+
if fn.startswith(shared.assets_path):
|
23 |
+
web_path = os.path.relpath(fn, shared.chuanhu_path).replace('\\', '/')
|
24 |
+
else:
|
25 |
+
web_path = os.path.abspath(fn)
|
26 |
+
return f'file={web_path}?{os.path.getmtime(fn)}'
|
27 |
+
|
28 |
+
ScriptFile = namedtuple("ScriptFile", ["basedir", "filename", "path"])
|
29 |
+
|
30 |
+
def javascript_html():
|
31 |
+
head = ""
|
32 |
+
for script in list_scripts("javascript", ".js"):
|
33 |
+
head += f'<script type="text/javascript" src="{webpath(script.path)}"></script>\n'
|
34 |
+
for script in list_scripts("javascript", ".mjs"):
|
35 |
+
head += f'<script type="module" src="{webpath(script.path)}"></script>\n'
|
36 |
+
return head
|
37 |
+
|
38 |
+
def css_html():
|
39 |
+
head = ""
|
40 |
+
for cssfile in list_scripts("stylesheet", ".css"):
|
41 |
+
head += f'<link rel="stylesheet" property="stylesheet" href="{webpath(cssfile.path)}">'
|
42 |
+
return head
|
43 |
+
|
44 |
+
def list_scripts(scriptdirname, extension):
|
45 |
+
scripts_list = []
|
46 |
+
scripts_dir = os.path.join(shared.chuanhu_path, "web_assets", scriptdirname)
|
47 |
+
if os.path.exists(scripts_dir):
|
48 |
+
for filename in sorted(os.listdir(scripts_dir)):
|
49 |
+
scripts_list.append(ScriptFile(shared.assets_path, filename, os.path.join(scripts_dir, filename)))
|
50 |
+
scripts_list = [x for x in scripts_list if os.path.splitext(x.path)[1].lower() == extension and os.path.isfile(x.path)]
|
51 |
+
return scripts_list
|
52 |
+
|
53 |
+
|
54 |
+
def reload_javascript():
|
55 |
+
js = javascript_html()
|
56 |
+
js += '<script async type="module" src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>'
|
57 |
+
js += '<script async type="module" src="http://spin.js.org/spin.umd.js"></script><link type="text/css" href="https://spin.js.org/spin.css" rel="stylesheet" />'
|
58 |
+
|
59 |
+
css = css_html()
|
60 |
+
|
61 |
+
def template_response(*args, **kwargs):
|
62 |
+
res = GradioTemplateResponseOriginal(*args, **kwargs)
|
63 |
+
res.body = res.body.replace(b'</head>', f'{js}</head>'.encode("utf8"))
|
64 |
+
res.body = res.body.replace(b'</body>', f'{css}</body>'.encode("utf8"))
|
65 |
+
res.init_headers()
|
66 |
+
return res
|
67 |
+
|
68 |
+
gr.routes.templates.TemplateResponse = template_response
|
69 |
+
|
70 |
+
GradioTemplateResponseOriginal = gr.routes.templates.TemplateResponse
|
readme/README_en.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
<h1 align="center">川虎 Chat 🐯 Chuanhu Chat</h1>
|
7 |
<div align="center">
|
8 |
<a href="https://github.com/GaiZhenBiao/ChuanhuChatGPT">
|
9 |
-
<img src="https://
|
10 |
</a>
|
11 |
|
12 |
<p align="center">
|
@@ -44,6 +44,23 @@
|
|
44 |
</p>
|
45 |
</div>
|
46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
## Usage Tips
|
48 |
|
49 |
- To better control the ChatGPT, use System Prompt.
|
@@ -51,11 +68,11 @@
|
|
51 |
- To try again if the response is unsatisfactory, use `🔄 Regenerate` button.
|
52 |
- To start a new line in the input box, press <kbd>Shift</kbd> + <kbd>Enter</kbd> keys.
|
53 |
- To quickly switch between input history, press <kbd>↑</kbd> and <kbd>↓</kbd> key in the input box.
|
54 |
-
- To deploy the program onto a server,
|
55 |
-
- To get a public shared link,
|
56 |
- To use it in Hugging Face Spaces: It is recommended to **Duplicate Space** and run the program in your own Space for a faster and more secure experience.
|
57 |
|
58 |
-
##
|
59 |
|
60 |
```shell
|
61 |
git clone https://github.com/GaiZhenbiao/ChuanhuChatGPT.git
|
@@ -87,10 +104,6 @@ When you encounter problems, you should try manually pulling the latest changes
|
|
87 |
```
|
88 |
pip install -r requirements.txt
|
89 |
```
|
90 |
-
3. Update Gradio
|
91 |
-
```
|
92 |
-
pip install gradio --upgrade --force-reinstall
|
93 |
-
```
|
94 |
|
95 |
Generally, you can solve most problems by following these steps.
|
96 |
|
|
|
6 |
<h1 align="center">川虎 Chat 🐯 Chuanhu Chat</h1>
|
7 |
<div align="center">
|
8 |
<a href="https://github.com/GaiZhenBiao/ChuanhuChatGPT">
|
9 |
+
<img src="https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/70903329/aca3a7ec-4f1d-4667-890c-a6f47bf08f63" alt="Logo" height="156">
|
10 |
</a>
|
11 |
|
12 |
<p align="center">
|
|
|
44 |
</p>
|
45 |
</div>
|
46 |
|
47 |
+
## Supported LLM Models
|
48 |
+
|
49 |
+
**LLM models via API**:
|
50 |
+
|
51 |
+
- [ChatGPT](https://chat.openai.com) ([GPT-4](https://openai.com/product/gpt-4))
|
52 |
+
- [Google PaLM](https://developers.generativeai.google/products/palm)
|
53 |
+
- [Inspur Yuan 1.0](https://air.inspur.com/home)
|
54 |
+
- [MiniMax](https://api.minimax.chat/)
|
55 |
+
- [XMChat](https://github.com/MILVLG/xmchat)
|
56 |
+
|
57 |
+
**LLM models via local deployment**:
|
58 |
+
|
59 |
+
- [ChatGLM](https://github.com/THUDM/ChatGLM-6B) ([ChatGLM2](https://github.com/THUDM/ChatGLM2-6B))
|
60 |
+
- [LLaMA](https://github.com/facebookresearch/llama)
|
61 |
+
- [StableLM](https://github.com/Stability-AI/StableLM)
|
62 |
+
- [MOSS](https://github.com/OpenLMLab/MOSS)
|
63 |
+
|
64 |
## Usage Tips
|
65 |
|
66 |
- To better control the ChatGPT, use System Prompt.
|
|
|
68 |
- To try again if the response is unsatisfactory, use `🔄 Regenerate` button.
|
69 |
- To start a new line in the input box, press <kbd>Shift</kbd> + <kbd>Enter</kbd> keys.
|
70 |
- To quickly switch between input history, press <kbd>↑</kbd> and <kbd>↓</kbd> key in the input box.
|
71 |
+
- To deploy the program onto a server, set `"server_name": "0.0.0.0", "server_port" <your port number>,` in `config.json`.
|
72 |
+
- To get a public shared link, set `"share": true,` in `config.json`. Please be noted that the program must be running in order to be accessed via a public link.
|
73 |
- To use it in Hugging Face Spaces: It is recommended to **Duplicate Space** and run the program in your own Space for a faster and more secure experience.
|
74 |
|
75 |
+
## Quickstart
|
76 |
|
77 |
```shell
|
78 |
git clone https://github.com/GaiZhenbiao/ChuanhuChatGPT.git
|
|
|
104 |
```
|
105 |
pip install -r requirements.txt
|
106 |
```
|
|
|
|
|
|
|
|
|
107 |
|
108 |
Generally, you can solve most problems by following these steps.
|
109 |
|
readme/README_ja.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
<h1 align="center">川虎 Chat 🐯 Chuanhu Chat</h1>
|
7 |
<div align="center">
|
8 |
<a href="https://github.com/GaiZhenBiao/ChuanhuChatGPT">
|
9 |
-
<img src="https://
|
10 |
</a>
|
11 |
|
12 |
<p align="center">
|
@@ -44,17 +44,34 @@
|
|
44 |
</p>
|
45 |
</div>
|
46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
## 使う上でのTips
|
48 |
|
49 |
- ChatGPTをより適切に制御するために、システムプロンプトを使用できます。
|
50 |
- プロンプトテンプレートを使用するには、プロンプトテンプレートコレクションを選択し、ドロップダウンメニューから特定のプロンプトを選択。回答が不十分な場合は、`🔄再生成`ボタンを使って再試行します。
|
51 |
- 入力ボックスで改行するには、<kbd>Shift</kbd> + <kbd>Enter</kbd>キーを押してください。
|
52 |
- 入力履歴を素早く切り替えるには、入力ボックスで <kbd>↑</kbd>と<kbd>↓</kbd>キーを押す。
|
53 |
-
-
|
54 |
-
-
|
55 |
- Hugging Face Spacesで使用する場合: より速く、より安全に利用するために、**Duplicate Space**を使用し、自分のスペースでプログラムを実行することをお勧めします。
|
56 |
|
57 |
-
##
|
58 |
|
59 |
```shell
|
60 |
git clone https://github.com/GaiZhenbiao/ChuanhuChatGPT.git
|
@@ -86,10 +103,6 @@ python ChuanhuChatbot.py
|
|
86 |
```
|
87 |
pip install -r requirements.txt
|
88 |
```
|
89 |
-
3. Gradioを更新
|
90 |
-
```
|
91 |
-
pip install gradio --upgrade --force-reinstall
|
92 |
-
```
|
93 |
|
94 |
一般的に、以下の手順でほとんどの問題を解決することができます。
|
95 |
|
|
|
6 |
<h1 align="center">川虎 Chat 🐯 Chuanhu Chat</h1>
|
7 |
<div align="center">
|
8 |
<a href="https://github.com/GaiZhenBiao/ChuanhuChatGPT">
|
9 |
+
<img src="https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/70903329/aca3a7ec-4f1d-4667-890c-a6f47bf08f63" alt="Logo" height="156">
|
10 |
</a>
|
11 |
|
12 |
<p align="center">
|
|
|
44 |
</p>
|
45 |
</div>
|
46 |
|
47 |
+
## サポートされている大規模言語モデル
|
48 |
+
|
49 |
+
**APIを通じてアクセス可能な大規模言語モデル**:
|
50 |
+
|
51 |
+
- [ChatGPT](https://chat.openai.com) ([GPT-4](https://openai.com/product/gpt-4))
|
52 |
+
- [Google PaLM](https://developers.generativeai.google/products/palm)
|
53 |
+
- [Inspur Yuan 1.0](https://air.inspur.com/home)
|
54 |
+
- [MiniMax](https://api.minimax.chat/)
|
55 |
+
- [XMChat](https://github.com/MILVLG/xmchat)
|
56 |
+
|
57 |
+
**ローカルに展開された大規模言語モデル**:
|
58 |
+
|
59 |
+
- [ChatGLM](https://github.com/THUDM/ChatGLM-6B) ([ChatGLM2](https://github.com/THUDM/ChatGLM2-6B))
|
60 |
+
- [LLaMA](https://github.com/facebookresearch/llama)
|
61 |
+
- [StableLM](https://github.com/Stability-AI/StableLM)
|
62 |
+
- [MOSS](https://github.com/OpenLMLab/MOSS)
|
63 |
+
|
64 |
## 使う上でのTips
|
65 |
|
66 |
- ChatGPTをより適切に制御するために、システムプロンプトを使用できます。
|
67 |
- プロンプトテンプレートを使用するには、プロンプトテンプレートコレクションを選択し、ドロップダウンメニューから特定のプロンプトを選択。回答が不十分な場合は、`🔄再生成`ボタンを使って再試行します。
|
68 |
- 入力ボックスで改行するには、<kbd>Shift</kbd> + <kbd>Enter</kbd>キーを押してください。
|
69 |
- 入力履歴を素早く切り替えるには、入力ボックスで <kbd>↑</kbd>と<kbd>↓</kbd>キーを押す。
|
70 |
+
- プログラムをサーバーに展開するには、`config.json` 内の `"server_name": "0.0.0.0", "server_port": <ポート番号>`を設定してください。
|
71 |
+
- 共有リンクを取得するには、 `config.json` 内の `"share": true` を設定してください。なお、公開リンクでアクセスするためには、プログラムが実行されている必要があることに注意してください。
|
72 |
- Hugging Face Spacesで使用する場合: より速く、より安全に利用するために、**Duplicate Space**を使用し、自分のスペースでプログラムを実行することをお勧めします。
|
73 |
|
74 |
+
## クイックスタート
|
75 |
|
76 |
```shell
|
77 |
git clone https://github.com/GaiZhenbiao/ChuanhuChatGPT.git
|
|
|
103 |
```
|
104 |
pip install -r requirements.txt
|
105 |
```
|
|
|
|
|
|
|
|
|
106 |
|
107 |
一般的に、以下の手順でほとんどの問題を解決することができます。
|
108 |
|
requirements.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
-
gradio==3.
|
2 |
-
gradio_client==0.
|
3 |
pypinyin
|
4 |
tiktoken
|
5 |
socksio
|
@@ -7,7 +7,7 @@ tqdm
|
|
7 |
colorama
|
8 |
googlesearch-python
|
9 |
Pygments
|
10 |
-
langchain==0.0.
|
11 |
markdown
|
12 |
PyPDF2
|
13 |
pdfplumber
|
@@ -16,10 +16,13 @@ commentjson
|
|
16 |
openpyxl
|
17 |
pandoc
|
18 |
wolframalpha
|
19 |
-
faiss-cpu
|
20 |
duckduckgo-search
|
21 |
arxiv
|
22 |
wikipedia
|
23 |
google.generativeai
|
24 |
-
openai
|
25 |
unstructured
|
|
|
|
|
|
|
|
1 |
+
gradio==3.40.0
|
2 |
+
gradio_client==0.4.0
|
3 |
pypinyin
|
4 |
tiktoken
|
5 |
socksio
|
|
|
7 |
colorama
|
8 |
googlesearch-python
|
9 |
Pygments
|
10 |
+
langchain==0.0.276
|
11 |
markdown
|
12 |
PyPDF2
|
13 |
pdfplumber
|
|
|
16 |
openpyxl
|
17 |
pandoc
|
18 |
wolframalpha
|
19 |
+
faiss-cpu==1.7.4
|
20 |
duckduckgo-search
|
21 |
arxiv
|
22 |
wikipedia
|
23 |
google.generativeai
|
24 |
+
openai>=0.27.9
|
25 |
unstructured
|
26 |
+
google-api-python-client
|
27 |
+
tabulate
|
28 |
+
ujson
|
requirements_advanced.txt
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
transformers
|
2 |
+
huggingface_hub
|
3 |
+
torch
|
4 |
+
icetk
|
5 |
+
protobuf==3.19.0
|
6 |
+
git+https://github.com/OptimalScale/LMFlow.git
|
7 |
+
cpm-kernels
|
8 |
+
sentence_transformers
|
9 |
+
accelerate
|
10 |
+
sentencepiece
|
11 |
+
datasets
|