Spaces:
Sleeping
Sleeping
JohnSmith9982
commited on
Commit
•
fcf8580
1
Parent(s):
8993361
Upload 63 files
Browse files- ChuanhuChatbot.py +109 -58
- config_example.json +48 -14
- modules/config.py +106 -27
- modules/index_func.py +13 -5
- 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 +127 -28
- modules/models/models.py +27 -15
- modules/overwrites.py +33 -20
- modules/presets.py +9 -2
- modules/repo.py +215 -0
- modules/shared.py +1 -0
- modules/utils.py +57 -53
- modules/webui.py +70 -0
- requirements.txt +4 -3
- requirements_advanced.txt +11 -0
- web_assets/favicon.ico +0 -0
- web_assets/html/appearance_switcher.html +6 -0
- web_assets/html/billing_info.html +9 -0
- web_assets/html/footer.html +1 -0
- web_assets/html/update.html +29 -0
- web_assets/javascript/ChuanhuChat.js +317 -0
- web_assets/javascript/chat-history.js +62 -0
- web_assets/javascript/external-scripts.js +2 -0
- web_assets/javascript/localization.js +67 -0
- web_assets/javascript/message-button.js +97 -0
- web_assets/javascript/sliders.js +22 -0
- web_assets/javascript/updater.js +202 -0
- web_assets/javascript/user-info.js +67 -0
- web_assets/javascript/utils.js +67 -0
- web_assets/stylesheet/ChuanhuChat.css +105 -0
- web_assets/stylesheet/chatbot.css +242 -0
- web_assets/stylesheet/custom-components.css +240 -0
- web_assets/stylesheet/markdown.css +61 -0
- web_assets/stylesheet/override-gradio.css +67 -0
ChuanhuChatbot.py
CHANGED
@@ -10,6 +10,8 @@ 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 +19,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,27 +36,36 @@ 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 |
emptyBtn = gr.Button(
|
57 |
-
i18n("🧹 新的对话"), elem_id="
|
58 |
)
|
59 |
retryBtn = gr.Button(i18n("🔄 重新生成"))
|
60 |
delFirstBtn = gr.Button(i18n("🗑️ 删除最旧对话"))
|
@@ -77,9 +88,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 +98,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 +118,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 +129,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 +141,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 +152,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 +167,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"))
|
@@ -160,11 +179,12 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
|
|
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 +212,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 +264,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 +354,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 +376,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 +427,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,7 +461,8 @@ 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 |
|
@@ -444,15 +481,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 +515,10 @@ if __name__ == "__main__":
|
|
469 |
reload_javascript()
|
470 |
demo.queue(concurrency_count=CONCURRENT_COUNT).launch(
|
471 |
blocked_paths=["config.json"],
|
472 |
-
|
|
|
|
|
|
|
|
|
|
|
473 |
)
|
|
|
10 |
from modules.utils import *
|
11 |
from modules.presets import *
|
12 |
from modules.overwrites import *
|
13 |
+
from modules.webui import *
|
14 |
+
from modules.repo import *
|
15 |
from modules.models.models import get_model
|
16 |
|
17 |
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
|
19 |
gr.Chatbot._postprocess_chat_messages = postprocess_chat_messages
|
20 |
gr.Chatbot.postprocess = postprocess
|
21 |
|
22 |
+
# with open("web_assets/css/ChuanhuChat.css", "r", encoding="utf-8") as f:
|
23 |
+
# ChuanhuChatCSS = f.read()
|
24 |
|
25 |
def create_new_model():
|
26 |
return get_model(model_name = MODELS[DEFAULT_MODEL], access_key = my_api_key)[0]
|
27 |
|
28 |
+
with gr.Blocks(theme=small_and_beautiful_theme) as demo:
|
29 |
user_name = gr.State("")
|
30 |
promptTemplates = gr.State(load_template(get_template_names(plain=True)[0], mode=2))
|
31 |
user_question = gr.State("")
|
|
|
36 |
topic = gr.State(i18n("未命名对话历史记录"))
|
37 |
|
38 |
with gr.Row():
|
39 |
+
gr.HTML(CHUANHU_TITLE, elem_id="app-title")
|
40 |
+
status_display = gr.Markdown(get_geoip(), elem_id="status-display")
|
41 |
+
with gr.Row(elem_id="float-display"):
|
42 |
+
user_info = gr.Markdown(value="getting user info...", elem_id="user-info")
|
43 |
+
update_info = gr.HTML(get_html("update.html").format(
|
44 |
+
current_version=repo_tag_html(),
|
45 |
+
version_time=version_time(),
|
46 |
+
cancel_btn=i18n("取消"),
|
47 |
+
update_btn=i18n("更新"),
|
48 |
+
seenew_btn=i18n("详情"),
|
49 |
+
ok_btn=i18n("好"),
|
50 |
+
), visible=check_update)
|
51 |
+
|
52 |
+
with gr.Row(equal_height=True):
|
53 |
with gr.Column(scale=5):
|
54 |
with gr.Row():
|
55 |
+
chatbot = gr.Chatbot(label="Chuanhu Chat", elem_id="chuanhu-chatbot", latex_delimiters=latex_delimiters_set, height=700)
|
56 |
with gr.Row():
|
57 |
with gr.Column(min_width=225, scale=12):
|
58 |
user_input = gr.Textbox(
|
59 |
+
elem_id="user-input-tb",
|
60 |
+
show_label=False, placeholder=i18n("在这里输入"),
|
61 |
+
container=False
|
62 |
+
)
|
63 |
with gr.Column(min_width=42, scale=1):
|
64 |
+
submitBtn = gr.Button(value="", variant="primary", elem_id="submit-btn")
|
65 |
+
cancelBtn = gr.Button(value="", variant="secondary", visible=False, elem_id="cancel-btn")
|
66 |
with gr.Row():
|
67 |
emptyBtn = gr.Button(
|
68 |
+
i18n("🧹 新的对话"), elem_id="empty-btn"
|
69 |
)
|
70 |
retryBtn = gr.Button(i18n("🔄 重新生成"))
|
71 |
delFirstBtn = gr.Button(i18n("🗑️ 删除最旧对话"))
|
|
|
88 |
label="API-Key",
|
89 |
)
|
90 |
if multi_api_key:
|
91 |
+
usageTxt = gr.Markdown(i18n("多账号模式已开启,无需输入key,可直接开始对话"), elem_id="usage-display", elem_classes="insert-block", visible=show_api_billing)
|
92 |
else:
|
93 |
+
usageTxt = gr.Markdown(i18n("**发送消息** 或 **提交key** 以显示额度"), elem_id="usage-display", elem_classes="insert-block", visible=show_api_billing)
|
94 |
model_select_dropdown = gr.Dropdown(
|
95 |
label=i18n("选择模型"), choices=MODELS, multiselect=False, value=MODELS[DEFAULT_MODEL], interactive=True
|
96 |
)
|
|
|
98 |
label=i18n("选择LoRA模型"), choices=[], multiselect=False, interactive=True, visible=False
|
99 |
)
|
100 |
with gr.Row():
|
101 |
+
single_turn_checkbox = gr.Checkbox(label=i18n("单轮对话"), value=False, elem_classes="switch-checkbox")
|
102 |
+
use_websearch_checkbox = gr.Checkbox(label=i18n("使用在线搜索"), value=False, elem_classes="switch-checkbox")
|
103 |
language_select_dropdown = gr.Dropdown(
|
104 |
label=i18n("选择回复语言(针对搜索&索引功能)"),
|
105 |
choices=REPLY_LANGUAGES,
|
106 |
multiselect=False,
|
107 |
value=REPLY_LANGUAGES[0],
|
108 |
)
|
109 |
+
index_files = gr.Files(label=i18n("上传"), type="file", elem_id="upload-index-file")
|
110 |
two_column = gr.Checkbox(label=i18n("双栏pdf"), value=advance_docs["pdf"].get("two_column", False))
|
111 |
summarize_btn = gr.Button(i18n("总结"))
|
112 |
# TODO: 公式ocr
|
|
|
118 |
placeholder=i18n("在这里输入System Prompt..."),
|
119 |
label="System prompt",
|
120 |
value=INITIAL_SYSTEM_PROMPT,
|
121 |
+
lines=10
|
122 |
+
)
|
123 |
with gr.Accordion(label=i18n("加载Prompt模板"), open=True):
|
124 |
with gr.Column():
|
125 |
with gr.Row():
|
|
|
129 |
choices=get_template_names(plain=True),
|
130 |
multiselect=False,
|
131 |
value=get_template_names(plain=True)[0],
|
132 |
+
container=False,
|
133 |
+
)
|
134 |
with gr.Column(scale=1):
|
135 |
templateRefreshBtn = gr.Button(i18n("🔄 刷新"))
|
136 |
with gr.Row():
|
|
|
141 |
get_template_names(plain=True)[0], mode=1
|
142 |
),
|
143 |
multiselect=False,
|
144 |
+
container=False,
|
145 |
+
)
|
146 |
|
147 |
with gr.Tab(label=i18n("保存/加载")):
|
148 |
with gr.Accordion(label=i18n("保存/加载对话历史记录"), open=True):
|
|
|
152 |
historyFileSelectDropdown = gr.Dropdown(
|
153 |
label=i18n("从列表中加载对话"),
|
154 |
choices=get_history_names(plain=True),
|
155 |
+
multiselect=False,
|
156 |
+
container=False,
|
157 |
)
|
158 |
+
with gr.Row():
|
159 |
+
with gr.Column(min_width=42, scale=1):
|
160 |
+
historyRefreshBtn = gr.Button(i18n("🔄 刷新"))
|
161 |
+
with gr.Column(min_width=42, scale=1):
|
162 |
+
historyDeleteBtn = gr.Button(i18n("🗑️ 删除"))
|
163 |
with gr.Row():
|
164 |
with gr.Column(scale=6):
|
165 |
saveFileName = gr.Textbox(
|
|
|
167 |
placeholder=i18n("设置文件名: 默认为.json,可选为.md"),
|
168 |
label=i18n("设置保存文件名"),
|
169 |
value=i18n("对话历史记录"),
|
170 |
+
elem_classes="no-container"
|
171 |
+
# container=False,
|
172 |
+
)
|
173 |
with gr.Column(scale=1):
|
174 |
saveHistoryBtn = gr.Button(i18n("💾 保存对话"))
|
175 |
exportMarkdownBtn = gr.Button(i18n("📝 导出为Markdown"))
|
|
|
179 |
downloadFile = gr.File(interactive=True)
|
180 |
|
181 |
with gr.Tab(label=i18n("高级")):
|
182 |
+
gr.HTML(get_html("appearance_switcher.html").format(label=i18n("切换亮暗色主题")), elem_classes="insert-block")
|
|
|
183 |
use_streaming_checkbox = gr.Checkbox(
|
184 |
+
label=i18n("实时传输回答"), value=True, visible=ENABLE_STREAMING_OPTION, elem_classes="switch-checkbox"
|
185 |
)
|
186 |
+
checkUpdateBtn = gr.Button(i18n("🔄 检查更新..."), visible=check_update)
|
187 |
+
gr.Markdown(i18n("# ⚠️ 务必谨慎更改 ⚠️"), elem_id="advanced-warning")
|
188 |
with gr.Accordion(i18n("参数"), open=False):
|
189 |
temperature_slider = gr.Slider(
|
190 |
minimum=-0,
|
|
|
212 |
)
|
213 |
stop_sequence_txt = gr.Textbox(
|
214 |
show_label=True,
|
215 |
+
placeholder=i18n("停止符,用英文逗号隔开..."),
|
216 |
label="stop",
|
217 |
value="",
|
218 |
lines=1,
|
|
|
264 |
lines=1,
|
265 |
)
|
266 |
|
267 |
+
with gr.Accordion(i18n("网络参数"), open=False):
|
268 |
+
gr.Markdown(i18n("---\n⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置"), elem_id="netsetting-warning")
|
269 |
+
default_btn = gr.Button(i18n("🔙 恢复默认网络设置"))
|
270 |
+
# 网络代理
|
271 |
+
proxyTxt = gr.Textbox(
|
272 |
+
show_label=True,
|
273 |
+
placeholder=i18n("未设置代理..."),
|
274 |
+
label=i18n("代理地址"),
|
275 |
+
value=config.http_proxy,
|
276 |
+
lines=1,
|
277 |
+
interactive=False,
|
278 |
+
# container=False,
|
279 |
+
elem_classes="view-only-textbox no-container",
|
280 |
+
)
|
281 |
+
# changeProxyBtn = gr.Button(i18n("🔄 设置代理地址"))
|
282 |
+
|
283 |
# 优先展示自定义的api_host
|
284 |
apihostTxt = gr.Textbox(
|
285 |
show_label=True,
|
286 |
+
placeholder="api.openai.com",
|
287 |
+
label="OpenAI API-Host",
|
288 |
value=config.api_host or shared.API_HOST,
|
289 |
lines=1,
|
290 |
+
interactive=False,
|
291 |
+
# container=False,
|
292 |
+
elem_classes="view-only-textbox no-container",
|
293 |
)
|
294 |
+
# changeAPIURLBtn = gr.Button(i18n("🔄 切换API地址"))
|
295 |
+
|
296 |
+
updateChuanhuBtn = gr.Button(visible=False, elem_classes="invisible-btn", elem_id="update-chuanhu-btn")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
297 |
|
298 |
gr.Markdown(CHUANHU_DESCRIPTION, elem_id="description")
|
299 |
gr.HTML(get_html("footer.html").format(versions=versions_html()), elem_id="footer")
|
|
|
354 |
outputs=[saveFileName, systemPromptTxt, chatbot]
|
355 |
)
|
356 |
|
357 |
+
refresh_history_args = dict(
|
358 |
+
fn=get_history_names, inputs=[gr.State(False), user_name], outputs=[historyFileSelectDropdown]
|
359 |
+
)
|
360 |
+
|
361 |
|
362 |
# Chatbot
|
363 |
cancelBtn.click(interrupt, [current_model], [])
|
|
|
376 |
inputs=[current_model],
|
377 |
outputs=[chatbot, status_display],
|
378 |
show_progress=True,
|
379 |
+
_js='clearHistoryHtml',
|
380 |
)
|
381 |
|
382 |
retryBtn.click(**start_outputing_args).then(
|
|
|
427 |
keyTxt.change(set_key, [current_model, keyTxt], [user_api_key, status_display], api_name="set_key").then(**get_usage_args)
|
428 |
keyTxt.submit(**get_usage_args)
|
429 |
single_turn_checkbox.change(set_single_turn, [current_model, single_turn_checkbox], None)
|
430 |
+
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")
|
431 |
model_select_dropdown.change(toggle_like_btn_visibility, [model_select_dropdown], [like_dislike_area], show_progress=False)
|
432 |
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)
|
433 |
|
|
|
461 |
downloadFile,
|
462 |
show_progress=True,
|
463 |
)
|
464 |
+
historyRefreshBtn.click(**refresh_history_args)
|
465 |
+
historyDeleteBtn.click(delete_chat_history, [current_model, historyFileSelectDropdown, user_name], [status_display, historyFileSelectDropdown, chatbot], _js='(a,b,c)=>{return showConfirmationDialog(a, b, c);}')
|
466 |
historyFileSelectDropdown.change(**load_history_from_file_args)
|
467 |
downloadFile.change(upload_chat_history, [current_model, downloadFile, user_name], [saveFileName, systemPromptTxt, chatbot])
|
468 |
|
|
|
481 |
default_btn.click(
|
482 |
reset_default, [], [apihostTxt, proxyTxt, status_display], show_progress=True
|
483 |
)
|
484 |
+
# changeAPIURLBtn.click(
|
485 |
+
# change_api_host,
|
486 |
+
# [apihostTxt],
|
487 |
+
# [status_display],
|
488 |
+
# show_progress=True,
|
489 |
+
# )
|
490 |
+
# changeProxyBtn.click(
|
491 |
+
# change_proxy,
|
492 |
+
# [proxyTxt],
|
493 |
+
# [status_display],
|
494 |
+
# show_progress=True,
|
495 |
+
# )
|
496 |
+
checkUpdateBtn.click(fn=None, _js='manualCheckUpdate')
|
497 |
+
|
498 |
+
# Invisible elements
|
499 |
+
updateChuanhuBtn.click(
|
500 |
+
update_chuanhu,
|
501 |
+
[],
|
502 |
[status_display],
|
503 |
show_progress=True,
|
504 |
)
|
|
|
515 |
reload_javascript()
|
516 |
demo.queue(concurrency_count=CONCURRENT_COUNT).launch(
|
517 |
blocked_paths=["config.json"],
|
518 |
+
server_name=server_name,
|
519 |
+
server_port=server_port,
|
520 |
+
share=share,
|
521 |
+
auth=auth_list if authflag else None,
|
522 |
+
favicon_path="./web_assets/favicon.ico",
|
523 |
+
inbrowser=not dockerflag, # 禁止在docker下开启inbrowser
|
524 |
)
|
config_example.json
CHANGED
@@ -1,24 +1,53 @@
|
|
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 +55,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 |
+
|
11 |
+
//== Azure ==
|
12 |
+
"openai_api_type": "openai", // 可选项:azure, openai
|
13 |
+
"azure_openai_api_key": "", // 你的 Azure OpenAI API Key,用于 Azure OpenAI 对话模型
|
14 |
+
"azure_openai_api_base_url": "", // 你的 Azure Base URL
|
15 |
+
"azure_openai_api_version": "2023-05-15", // 你的 Azure OpenAI API 版本
|
16 |
+
"azure_deployment_name": "", // 你的 Azure OpenAI Chat 模型 Deployment 名称
|
17 |
+
"azure_embedding_deployment_name": "", // 你的 Azure OpenAI Embedding 模型 Deployment 名称
|
18 |
+
"azure_embedding_model_name": "text-embedding-ada-002", // 你的 Azure OpenAI Embedding 模型名称
|
19 |
+
|
20 |
+
//== 基础配置 ==
|
21 |
+
"language": "auto", // 界面语言,可选"auto", "zh-CN", "en-US", "ja-JP", "ko-KR", "sv-SE"
|
22 |
"users": [], // 用户列表,[[用户名1, 密码1], [用户名2, 密码2], ...]
|
23 |
"local_embedding": false, //是否在本地编制索引
|
24 |
+
"hide_history_when_not_logged_in": false, //未登录情况下是否不展示对话历史
|
25 |
+
"check_update": true, //是否启用检查更新
|
26 |
"default_model": "gpt-3.5-turbo", // 默认模型
|
27 |
+
|
28 |
+
//== API 用量 ==
|
29 |
+
"show_api_billing": false, //是否显示OpenAI API用量(启用需要填写sensitive_id)
|
30 |
+
"sensitive_id": "", // 你 OpenAI 账户的 Sensitive ID,用于查询 API 用量
|
31 |
+
"usage_limit": 120, // 该 OpenAI API Key 的当月限额,单位:美元,用于计算百分比和显示上限
|
32 |
+
"legacy_api_usage": false, // 是否使用旧版 API 用量查询接口(OpenAI现已关闭该接口,但是如果你在使用第三方 API,第三方可能仍然支持此接口)
|
33 |
+
|
34 |
+
//== 川虎助理设置 ==
|
35 |
+
"default_chuanhu_assistant_model": "gpt-4", //川虎助理使用的模型,可选gpt-3.5-turbo或者gpt-4等
|
36 |
+
"GOOGLE_CSE_ID": "", //谷歌搜索引擎ID,用于川虎助理Pro模式,获取方式请看 https://stackoverflow.com/questions/37083058/programmatically-searching-google-in-python-using-custom-search
|
37 |
+
"GOOGLE_API_KEY": "", //谷歌API Key,用于川虎助理Pro模式
|
38 |
+
"WOLFRAM_ALPHA_APPID": "", //Wolfram Alpha API Key,用于川虎助理Pro模式,获取方式请看 https://products.wolframalpha.com/api/
|
39 |
+
"SERPAPI_API_KEY": "", //SerpAPI API Key,用于川虎助理Pro模式,获取方式请看 https://serpapi.com/
|
40 |
+
|
41 |
+
//== 文档处理与显示 ==
|
42 |
+
"latex_option": "default", // LaTeX 公式渲染策略,可选"default", "strict", "all"或者"disabled"
|
43 |
"advance_docs": {
|
44 |
"pdf": {
|
45 |
+
"two_column": false, // 是否认为PDF是双栏的
|
46 |
+
"formula_ocr": true // 是否使用OCR识别PDF中的公式
|
|
|
|
|
47 |
}
|
48 |
},
|
49 |
+
|
50 |
+
//== 高级配置 ==
|
51 |
// 是否多个API Key轮换使用
|
52 |
"multi_api_key": false,
|
53 |
"api_key_list": [
|
|
|
55 |
"sk-xxxxxxxxxxxxxxxxxxxxxxxx2",
|
56 |
"sk-xxxxxxxxxxxxxxxxxxxxxxxx3"
|
57 |
],
|
58 |
+
// 自定义OpenAI API Base
|
59 |
+
// "openai_api_base": "https://api.openai.com",
|
60 |
+
// 自定义使用代理(请替换代理URL)
|
61 |
+
// "https_proxy": "http://127.0.0.1:1079",
|
62 |
+
// "http_proxy": "http://127.0.0.1:1079",
|
63 |
+
// 自定义端口、自定义ip(请替换对应内容)
|
64 |
// "server_name": "0.0.0.0",
|
65 |
// "server_port": 7860,
|
66 |
// 如果要share到gradio,设置为true
|
modules/config.py
CHANGED
@@ -11,6 +11,7 @@ from . import presets
|
|
11 |
|
12 |
__all__ = [
|
13 |
"my_api_key",
|
|
|
14 |
"authflag",
|
15 |
"auth_list",
|
16 |
"dockerflag",
|
@@ -23,8 +24,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 +39,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 +68,39 @@ 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 +110,14 @@ 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 +125,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 +154,26 @@ def retrieve_openai_api(api_key = None):
|
|
117 |
yield api_key
|
118 |
os.environ["OPENAI_API_KEY"] = old_api_key
|
119 |
|
120 |
-
|
|
|
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 |
-
http_proxy =
|
129 |
-
https_proxy =
|
130 |
-
http_proxy =
|
131 |
-
https_proxy =
|
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 +190,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:
|
|
|
11 |
|
12 |
__all__ = [
|
13 |
"my_api_key",
|
14 |
+
"sensitive_id",
|
15 |
"authflag",
|
16 |
"auth_list",
|
17 |
"dockerflag",
|
|
|
24 |
"server_name",
|
25 |
"server_port",
|
26 |
"share",
|
27 |
+
"check_update",
|
28 |
+
"latex_delimiters_set",
|
29 |
"hide_history_when_not_logged_in",
|
30 |
+
"default_chuanhu_assistant_model",
|
31 |
+
"show_api_billing"
|
32 |
]
|
33 |
|
34 |
# 添加一个统一的config文件,避免文件过多造成的疑惑(优先级最低)
|
|
|
39 |
else:
|
40 |
config = {}
|
41 |
|
42 |
+
|
43 |
+
def load_config_to_environ(key_list):
|
44 |
+
global config
|
45 |
+
for key in key_list:
|
46 |
+
if key in config:
|
47 |
+
os.environ[key.upper()] = os.environ.get(key.upper(), config[key])
|
48 |
+
|
49 |
+
|
50 |
lang_config = config.get("language", "auto")
|
51 |
language = os.environ.get("LANGUAGE", lang_config)
|
52 |
|
53 |
+
hide_history_when_not_logged_in = config.get(
|
54 |
+
"hide_history_when_not_logged_in", False)
|
55 |
+
check_update = config.get("check_update", True)
|
56 |
+
show_api_billing = config.get("show_api_billing", False)
|
57 |
+
show_api_billing = bool(os.environ.get("SHOW_API_BILLING", show_api_billing))
|
58 |
|
59 |
if os.path.exists("api_key.txt"):
|
60 |
logging.info("检测到api_key.txt文件,正在进行迁移...")
|
|
|
68 |
logging.info("检测到auth.json文件,正在进行迁移...")
|
69 |
auth_list = []
|
70 |
with open("auth.json", "r", encoding='utf-8') as f:
|
71 |
+
auth = json.load(f)
|
72 |
+
for _ in auth:
|
73 |
+
if auth[_]["username"] and auth[_]["password"]:
|
74 |
+
auth_list.append((auth[_]["username"], auth[_]["password"]))
|
75 |
+
else:
|
76 |
+
logging.error("请检查auth.json文件中的用户名和密码!")
|
77 |
+
sys.exit(1)
|
78 |
config["users"] = auth_list
|
79 |
os.rename("auth.json", "auth(deprecated).json")
|
80 |
with open("config.json", "w", encoding='utf-8') as f:
|
81 |
json.dump(config, f, indent=4, ensure_ascii=False)
|
82 |
|
83 |
+
# 处理docker if we are running in Docker
|
84 |
dockerflag = config.get("dockerflag", False)
|
85 |
if os.environ.get("dockerrun") == "yes":
|
86 |
dockerflag = True
|
87 |
|
88 |
+
# 处理 api-key 以及 允许的用户列表
|
89 |
my_api_key = config.get("openai_api_key", "")
|
90 |
my_api_key = os.environ.get("OPENAI_API_KEY", my_api_key)
|
91 |
+
os.environ["OPENAI_API_KEY"] = my_api_key
|
92 |
+
os.environ["OPENAI_EMBEDDING_API_KEY"] = my_api_key
|
93 |
+
|
94 |
+
if config.get("legacy_api_usage", False):
|
95 |
+
sensitive_id = config.get("sensitive_id", "")
|
96 |
+
sensitive_id = os.environ.get("SENSITIVE_ID", sensitive_id)
|
97 |
+
else:
|
98 |
+
sensitive_id = my_api_key
|
99 |
+
|
100 |
+
google_palm_api_key = config.get("google_palm_api_key", "")
|
101 |
+
google_palm_api_key = os.environ.get(
|
102 |
+
"GOOGLE_PALM_API_KEY", google_palm_api_key)
|
103 |
+
os.environ["GOOGLE_PALM_API_KEY"] = google_palm_api_key
|
104 |
|
105 |
xmchat_api_key = config.get("xmchat_api_key", "")
|
106 |
os.environ["XMCHAT_API_KEY"] = xmchat_api_key
|
|
|
110 |
minimax_group_id = config.get("minimax_group_id", "")
|
111 |
os.environ["MINIMAX_GROUP_ID"] = minimax_group_id
|
112 |
|
113 |
+
load_config_to_environ(["openai_api_type", "azure_openai_api_key", "azure_openai_api_base_url",
|
114 |
+
"azure_openai_api_version", "azure_deployment_name", "azure_embedding_deployment_name", "azure_embedding_model_name"])
|
115 |
+
|
116 |
|
117 |
usage_limit = os.environ.get("USAGE_LIMIT", config.get("usage_limit", 120))
|
118 |
|
119 |
+
# 多账户机制
|
120 |
+
multi_api_key = config.get("multi_api_key", False) # 是否开启多账户机制
|
121 |
if multi_api_key:
|
122 |
api_key_list = config.get("api_key_list", [])
|
123 |
if len(api_key_list) == 0:
|
|
|
125 |
sys.exit(1)
|
126 |
shared.state.set_api_key_queue(api_key_list)
|
127 |
|
128 |
+
auth_list = config.get("users", []) # 实际上是使用者的列表
|
129 |
authflag = len(auth_list) > 0 # 是否开启认证的状态值,改为判断auth_list长度
|
130 |
|
131 |
# 处理自定义的api_host,优先读环境变量的配置,如果存在则自动装配
|
132 |
+
api_host = os.environ.get(
|
133 |
+
"OPENAI_API_BASE", config.get("openai_api_base", None))
|
134 |
if api_host is not None:
|
135 |
shared.state.set_api_host(api_host)
|
136 |
+
os.environ["OPENAI_API_BASE"] = f"{api_host}/v1"
|
137 |
+
logging.info(f"OpenAI API Base set to: {os.environ['OPENAI_API_BASE']}")
|
138 |
|
139 |
+
default_chuanhu_assistant_model = config.get(
|
140 |
+
"default_chuanhu_assistant_model", "gpt-3.5-turbo")
|
141 |
for x in ["GOOGLE_CSE_ID", "GOOGLE_API_KEY", "WOLFRAM_ALPHA_APPID", "SERPAPI_API_KEY"]:
|
142 |
if config.get(x, None) is not None:
|
143 |
os.environ[x] = config[x]
|
144 |
|
145 |
+
|
146 |
@contextmanager
|
147 |
+
def retrieve_openai_api(api_key=None):
|
148 |
old_api_key = os.environ.get("OPENAI_API_KEY", "")
|
149 |
if api_key is None:
|
150 |
os.environ["OPENAI_API_KEY"] = my_api_key
|
|
|
154 |
yield api_key
|
155 |
os.environ["OPENAI_API_KEY"] = old_api_key
|
156 |
|
157 |
+
|
158 |
+
# 处理log
|
159 |
log_level = config.get("log_level", "INFO")
|
160 |
logging.basicConfig(
|
161 |
level=log_level,
|
162 |
format="%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s",
|
163 |
)
|
164 |
|
165 |
+
# 处理代理:
|
166 |
+
http_proxy = os.environ.get("HTTP_PROXY", "")
|
167 |
+
https_proxy = os.environ.get("HTTPS_PROXY", "")
|
168 |
+
http_proxy = config.get("http_proxy", http_proxy)
|
169 |
+
https_proxy = config.get("https_proxy", https_proxy)
|
170 |
|
171 |
# 重置系统变量,在不需要设置的时候不设置环境变量,以免引起全局代理报错
|
172 |
os.environ["HTTP_PROXY"] = ""
|
173 |
os.environ["HTTPS_PROXY"] = ""
|
174 |
|
175 |
+
local_embedding = config.get("local_embedding", False) # 是否使用本地embedding
|
176 |
+
|
177 |
|
178 |
@contextmanager
|
179 |
def retrieve_proxy(proxy=None):
|
|
|
190 |
old_var = os.environ["HTTP_PROXY"], os.environ["HTTPS_PROXY"]
|
191 |
os.environ["HTTP_PROXY"] = http_proxy
|
192 |
os.environ["HTTPS_PROXY"] = https_proxy
|
193 |
+
yield http_proxy, https_proxy # return new proxy
|
194 |
|
195 |
# return old proxy
|
196 |
os.environ["HTTP_PROXY"], os.environ["HTTPS_PROXY"] = old_var
|
197 |
|
198 |
|
199 |
+
# 处理latex options
|
200 |
+
user_latex_option = config.get("latex_option", "default")
|
201 |
+
if user_latex_option == "default":
|
202 |
+
latex_delimiters_set = [
|
203 |
+
{"left": "$$", "right": "$$", "display": True},
|
204 |
+
{"left": "$", "right": "$", "display": False},
|
205 |
+
{"left": "\\(", "right": "\\)", "display": False},
|
206 |
+
{"left": "\\[", "right": "\\]", "display": True},
|
207 |
+
]
|
208 |
+
elif user_latex_option == "strict":
|
209 |
+
latex_delimiters_set = [
|
210 |
+
{"left": "$$", "right": "$$", "display": True},
|
211 |
+
{"left": "\\(", "right": "\\)", "display": False},
|
212 |
+
{"left": "\\[", "right": "\\]", "display": True},
|
213 |
+
]
|
214 |
+
elif user_latex_option == "all":
|
215 |
+
latex_delimiters_set = [
|
216 |
+
{"left": "$$", "right": "$$", "display": True},
|
217 |
+
{"left": "$", "right": "$", "display": False},
|
218 |
+
{"left": "\\(", "right": "\\)", "display": False},
|
219 |
+
{"left": "\\[", "right": "\\]", "display": True},
|
220 |
+
{"left": "\\begin{equation}", "right": "\\end{equation}", "display": True},
|
221 |
+
{"left": "\\begin{align}", "right": "\\end{align}", "display": True},
|
222 |
+
{"left": "\\begin{alignat}", "right": "\\end{alignat}", "display": True},
|
223 |
+
{"left": "\\begin{gather}", "right": "\\end{gather}", "display": True},
|
224 |
+
{"left": "\\begin{CD}", "right": "\\end{CD}", "display": True},
|
225 |
+
]
|
226 |
+
elif user_latex_option == "disabled":
|
227 |
+
latex_delimiters_set = []
|
228 |
+
else:
|
229 |
+
latex_delimiters_set = [
|
230 |
+
{"left": "$$", "right": "$$", "display": True},
|
231 |
+
{"left": "$", "right": "$", "display": False},
|
232 |
+
{"left": "\\(", "right": "\\)", "display": False},
|
233 |
+
{"left": "\\[", "right": "\\]", "display": True},
|
234 |
+
]
|
235 |
+
|
236 |
+
# 处理advance docs
|
237 |
advance_docs = defaultdict(lambda: defaultdict(dict))
|
238 |
advance_docs.update(config.get("advance_docs", {}))
|
239 |
+
|
240 |
+
|
241 |
def update_doc_config(two_column_pdf):
|
242 |
global advance_docs
|
243 |
advance_docs["pdf"]["two_column"] = two_column_pdf
|
244 |
|
245 |
logging.info(f"更新后的文件参数为:{advance_docs}")
|
246 |
|
247 |
+
|
248 |
+
# 处理gradio.launch参数
|
249 |
server_name = config.get("server_name", None)
|
250 |
server_port = config.get("server_port", None)
|
251 |
if server_name is None:
|
modules/index_func.py
CHANGED
@@ -47,11 +47,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 +73,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
|
@@ -115,10 +117,16 @@ def construct_index(
|
|
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)
|
|
|
47 |
pdftext = parse_pdf(filepath, two_column).text
|
48 |
except:
|
49 |
pdftext = ""
|
50 |
+
with open(filepath, "rb") as pdfFileObj:
|
51 |
pdfReader = PyPDF2.PdfReader(pdfFileObj)
|
52 |
for page in tqdm(pdfReader.pages):
|
53 |
pdftext += page.extract_text()
|
54 |
+
texts = [Document(page_content=pdftext,
|
55 |
+
metadata={"source": filepath})]
|
56 |
elif file_type == ".docx":
|
57 |
logging.debug("Loading Word...")
|
58 |
from langchain.document_loaders import UnstructuredWordDocumentLoader
|
|
|
73 |
text_list = excel_to_string(filepath)
|
74 |
texts = []
|
75 |
for elem in text_list:
|
76 |
+
texts.append(Document(page_content=elem,
|
77 |
+
metadata={"source": filepath}))
|
78 |
else:
|
79 |
logging.debug("Loading text file...")
|
80 |
from langchain.document_loaders import TextLoader
|
|
|
117 |
index_path = f"./index/{index_name}"
|
118 |
if local_embedding:
|
119 |
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
|
120 |
+
embeddings = HuggingFaceEmbeddings(
|
121 |
+
model_name="sentence-transformers/distiluse-base-multilingual-cased-v2")
|
122 |
else:
|
123 |
from langchain.embeddings import OpenAIEmbeddings
|
124 |
+
if os.environ.get("OPENAI_API_TYPE", "openai") == "openai":
|
125 |
+
embeddings = OpenAIEmbeddings(openai_api_base=os.environ.get(
|
126 |
+
"OPENAI_API_BASE", None), openai_api_key=os.environ.get("OPENAI_EMBEDDING_API_KEY", api_key))
|
127 |
+
else:
|
128 |
+
embeddings = OpenAIEmbeddings(deployment=os.environ["AZURE_EMBEDDING_DEPLOYMENT_NAME"], openai_api_key=os.environ["AZURE_OPENAI_API_KEY"],
|
129 |
+
model=os.environ["AZURE_EMBEDDING_MODEL_NAME"], openai_api_base=os.environ["AZURE_OPENAI_API_BASE_URL"], openai_api_type="azure")
|
130 |
if os.path.exists(index_path):
|
131 |
logging.info("找到了缓存的索引文件,加载中……")
|
132 |
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,8 @@ 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 +164,10 @@ 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 +177,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 +217,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 +229,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 +264,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 +292,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 +320,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 +347,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 +375,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 +404,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 +465,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 +599,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 +614,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 +658,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 +675,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 +702,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 +738,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 |
|
145 |
@classmethod
|
146 |
def get_type(cls, model_name: str):
|
|
|
164 |
model_type = ModelType.Minimax
|
165 |
elif "川虎助理" in model_name_lower:
|
166 |
model_type = ModelType.ChuanhuAgent
|
167 |
+
elif "palm" in model_name_lower:
|
168 |
+
model_type = ModelType.GooglePaLM
|
169 |
+
elif "azure" or "api" in model_name_lower:
|
170 |
+
model_type = ModelType.LangchainChat
|
171 |
else:
|
172 |
model_type = ModelType.Unknown
|
173 |
return model_type
|
|
|
177 |
def __init__(
|
178 |
self,
|
179 |
model_name,
|
180 |
+
system_prompt=INITIAL_SYSTEM_PROMPT,
|
181 |
temperature=1.0,
|
182 |
top_p=1.0,
|
183 |
n_choices=1,
|
|
|
217 |
conversations are stored in self.history, with the most recent question, in OpenAI format
|
218 |
should return a generator, each time give the next word (str) in the answer
|
219 |
"""
|
220 |
+
logging.warning(
|
221 |
+
"stream predict not implemented, using at once predict instead")
|
222 |
response, _ = self.get_answer_at_once()
|
223 |
yield response
|
224 |
|
|
|
229 |
the answer (str)
|
230 |
total token count (int)
|
231 |
"""
|
232 |
+
logging.warning(
|
233 |
+
"at once predict not implemented, using stream predict instead")
|
234 |
response_iter = self.get_answer_stream_iter()
|
235 |
count = 0
|
236 |
for response in response_iter:
|
|
|
264 |
stream_iter = self.get_answer_stream_iter()
|
265 |
|
266 |
if display_append:
|
267 |
+
display_append = '\n\n<hr class="append-display no-in-raw" />' + display_append
|
268 |
+
partial_text = ""
|
269 |
for partial_text in stream_iter:
|
270 |
chatbot[-1] = (chatbot[-1][0], partial_text + display_append)
|
271 |
self.all_token_counts[-1] += 1
|
|
|
292 |
self.history[-2] = construct_user(fake_input)
|
293 |
chatbot[-1] = (chatbot[-1][0], ai_reply + display_append)
|
294 |
if fake_input is not None:
|
295 |
+
self.all_token_counts[-1] += count_token(
|
296 |
+
construct_assistant(ai_reply))
|
297 |
else:
|
298 |
+
self.all_token_counts[-1] = total_token_count - \
|
299 |
+
sum(self.all_token_counts)
|
300 |
status_text = self.token_message()
|
301 |
return chatbot, status_text
|
302 |
|
|
|
320 |
from langchain.chat_models import ChatOpenAI
|
321 |
from langchain.callbacks import StdOutCallbackHandler
|
322 |
prompt_template = "Write a concise summary of the following:\n\n{text}\n\nCONCISE SUMMARY IN " + language + ":"
|
323 |
+
PROMPT = PromptTemplate(
|
324 |
+
template=prompt_template, input_variables=["text"])
|
325 |
llm = ChatOpenAI()
|
326 |
+
chain = load_summarize_chain(
|
327 |
+
llm, chain_type="map_reduce", return_intermediate_steps=True, map_prompt=PROMPT, combine_prompt=PROMPT)
|
328 |
+
summary = chain({"input_documents": list(index.docstore.__dict__[
|
329 |
+
"_dict"].values())}, return_only_outputs=True)["output_text"]
|
330 |
print(i18n("总结") + f": {summary}")
|
331 |
chatbot.append([i18n("上传了")+str(len(files))+"个文件", summary])
|
332 |
return chatbot, status
|
|
|
347 |
msg = "索引获取成功,生成回答中……"
|
348 |
logging.info(msg)
|
349 |
with retrieve_proxy():
|
350 |
+
retriever = VectorStoreRetriever(vectorstore=index, search_type="similarity_score_threshold", search_kwargs={
|
351 |
+
"k": 6, "score_threshold": 0.5})
|
352 |
+
relevant_documents = retriever.get_relevant_documents(
|
353 |
+
real_inputs)
|
354 |
+
reference_results = [[d.page_content.strip("�"), os.path.basename(
|
355 |
+
d.metadata["source"])] for d in relevant_documents]
|
356 |
reference_results = add_source_numbers(reference_results)
|
357 |
display_append = add_details(reference_results)
|
358 |
display_append = "\n\n" + "".join(display_append)
|
|
|
375 |
reference_results.append([result['body'], result['href']])
|
376 |
display_append.append(
|
377 |
# f"{idx+1}. [{domain_name}]({result['href']})\n"
|
378 |
+
f"<a href=\"{result['href']}\" target=\"_blank\">{idx+1}. {result['title']}</a>"
|
379 |
)
|
380 |
reference_results = add_source_numbers(reference_results)
|
381 |
+
# display_append = "<ol>\n\n" + "".join(display_append) + "</ol>"
|
382 |
+
display_append = '<div class = "source-a">' + \
|
383 |
+
"".join(display_append) + '</div>'
|
384 |
real_inputs = (
|
385 |
replace_today(WEBSEARCH_PTOMPT_TEMPLATE)
|
386 |
.replace("{query}", real_inputs)
|
|
|
404 |
|
405 |
status_text = "开始生成回答……"
|
406 |
logging.info(
|
407 |
+
"用户" + f"{self.user_identifier}" + "的输入为:" +
|
408 |
+
colorama.Fore.BLUE + f"{inputs}" + colorama.Style.RESET_ALL
|
409 |
)
|
410 |
if should_check_token_count:
|
411 |
yield chatbot + [(inputs, "")], status_text
|
412 |
if reply_language == "跟随问题语言(不稳定)":
|
413 |
reply_language = "the same language as the question, such as English, 中文, 日本語, Español, Français, or Deutsch."
|
414 |
|
415 |
+
limited_context, fake_inputs, display_append, inputs, chatbot = self.prepare_inputs(
|
416 |
+
real_inputs=inputs, use_websearch=use_websearch, files=files, reply_language=reply_language, chatbot=chatbot)
|
417 |
yield chatbot + [(fake_inputs, "")], status_text
|
418 |
|
419 |
if (
|
|
|
465 |
yield chatbot, status_text
|
466 |
except Exception as e:
|
467 |
traceback.print_exc()
|
468 |
+
status_text = STANDARD_ERROR_MSG + beautify_err_msg(str(e))
|
469 |
yield chatbot, status_text
|
470 |
|
471 |
if len(self.history) > 1 and self.history[-1]["content"] != inputs:
|
|
|
599 |
self.system_prompt = new_system_prompt
|
600 |
|
601 |
def set_key(self, new_access_key):
|
602 |
+
if "*" not in new_access_key:
|
603 |
+
self.api_key = new_access_key.strip()
|
604 |
+
msg = i18n("API密钥更改为了") + hide_middle_chars(self.api_key)
|
605 |
+
logging.info(msg)
|
606 |
+
return self.api_key, msg
|
607 |
+
else:
|
608 |
+
return gr.update(), gr.update()
|
609 |
|
610 |
def set_single_turn(self, new_single_turn):
|
611 |
self.single_turn = new_single_turn
|
|
|
614 |
self.history = []
|
615 |
self.all_token_counts = []
|
616 |
self.interrupted = False
|
617 |
+
pathlib.Path(os.path.join(HISTORY_DIR, self.user_identifier, new_auto_history_filename(
|
618 |
+
os.path.join(HISTORY_DIR, self.user_identifier)))).touch()
|
619 |
return [], self.token_message([0])
|
620 |
|
621 |
def delete_first_conversation(self):
|
|
|
658 |
|
659 |
def auto_save(self, chatbot):
|
660 |
history_file_path = get_history_filepath(self.user_identifier)
|
661 |
+
save_file(history_file_path, self.system_prompt,
|
662 |
+
self.history, chatbot, self.user_identifier)
|
663 |
|
664 |
def export_markdown(self, filename, chatbot, user_name):
|
665 |
if filename == "":
|
|
|
675 |
filename = filename.name
|
676 |
try:
|
677 |
if "/" not in filename:
|
678 |
+
history_file_path = os.path.join(
|
679 |
+
HISTORY_DIR, user_name, filename)
|
680 |
else:
|
681 |
history_file_path = filename
|
682 |
with open(history_file_path, "r", encoding="utf-8") as f:
|
|
|
702 |
logging.info(f"没有找到对话历史记录 {filename}")
|
703 |
return gr.update(), self.system_prompt, gr.update()
|
704 |
|
705 |
+
def delete_chat_history(self, filename, user_name):
|
706 |
+
if filename == "CANCELED":
|
707 |
+
return gr.update(), gr.update(), gr.update()
|
708 |
+
if filename == "":
|
709 |
+
return i18n("你没有选择任何对话历史"), gr.update(), gr.update()
|
710 |
+
if not filename.endswith(".json"):
|
711 |
+
filename += ".json"
|
712 |
+
if "/" not in filename:
|
713 |
+
history_file_path = os.path.join(HISTORY_DIR, user_name, filename)
|
714 |
+
else:
|
715 |
+
history_file_path = filename
|
716 |
+
try:
|
717 |
+
os.remove(history_file_path)
|
718 |
+
return i18n("删除对话历史成功"), get_history_names(False, user_name), []
|
719 |
+
except:
|
720 |
+
logging.info(f"删除对话历史失败 {history_file_path}")
|
721 |
+
return i18n("对话历史")+filename+i18n("已经被删除啦"), gr.update(), gr.update()
|
722 |
+
|
723 |
def auto_load(self):
|
724 |
if self.user_identifier == "":
|
725 |
self.reset()
|
726 |
return self.system_prompt, gr.update()
|
727 |
history_file_path = get_history_filepath(self.user_identifier)
|
728 |
+
filename, system_prompt, chatbot = self.load_chat_history(
|
729 |
+
history_file_path, self.user_identifier)
|
730 |
return system_prompt, chatbot
|
731 |
|
|
|
732 |
def like(self):
|
733 |
"""like the last response, implement if needed
|
734 |
"""
|
|
|
738 |
"""dislike the last response, implement if needed
|
739 |
"""
|
740 |
return gr.update()
|
741 |
+
|
742 |
+
|
743 |
+
class Base_Chat_Langchain_Client(BaseLLMModel):
|
744 |
+
def __init__(self, model_name, user_name=""):
|
745 |
+
super().__init__(model_name, user=user_name)
|
746 |
+
self.need_api_key = False
|
747 |
+
self.model = self.setup_model()
|
748 |
+
|
749 |
+
def setup_model(self):
|
750 |
+
# inplement this to setup the model then return it
|
751 |
+
pass
|
752 |
+
|
753 |
+
def _get_langchain_style_history(self):
|
754 |
+
history = [SystemMessage(content=self.system_prompt)]
|
755 |
+
for i in self.history:
|
756 |
+
if i["role"] == "user":
|
757 |
+
history.append(HumanMessage(content=i["content"]))
|
758 |
+
elif i["role"] == "assistant":
|
759 |
+
history.append(AIMessage(content=i["content"]))
|
760 |
+
return history
|
761 |
+
|
762 |
+
def get_answer_at_once(self):
|
763 |
+
assert isinstance(
|
764 |
+
self.model, BaseChatModel), "model is not instance of LangChain BaseChatModel"
|
765 |
+
history = self._get_langchain_style_history()
|
766 |
+
response = self.model.generate(history)
|
767 |
+
return response.content, sum(response.content)
|
768 |
+
|
769 |
+
def get_answer_stream_iter(self):
|
770 |
+
it = CallbackToIterator()
|
771 |
+
assert isinstance(
|
772 |
+
self.model, BaseChatModel), "model is not instance of LangChain BaseChatModel"
|
773 |
+
history = self._get_langchain_style_history()
|
774 |
+
|
775 |
+
def thread_func():
|
776 |
+
self.model(messages=history, callbacks=[
|
777 |
+
ChuanhuCallbackHandler(it.callback)])
|
778 |
+
it.finish()
|
779 |
+
t = Thread(target=thread_func)
|
780 |
+
t.start()
|
781 |
+
partial_text = ""
|
782 |
+
for value in it:
|
783 |
+
partial_text += value
|
784 |
+
yield partial_text
|
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,22 @@ 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
|
@@ -179,9 +180,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(
|
@@ -560,6 +562,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 +613,25 @@ 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 |
# return i18n("**本月使用金额** ") + f"\u3000 ${rounded_usage}"
|
100 |
+
return get_html("billing_info.html").format(
|
101 |
+
label = i18n("本月使用金额"),
|
102 |
+
usage_percent = usage_percent,
|
103 |
+
rounded_usage = rounded_usage,
|
104 |
+
usage_limit = usage_limit
|
105 |
+
)
|
|
|
|
|
|
|
106 |
except requests.exceptions.ConnectTimeout:
|
107 |
status_text = (
|
108 |
STANDARD_ERROR_MSG + CONNECTION_TIMEOUT_MSG + ERROR_RETRIEVE_MSG
|
|
|
180 |
def _refresh_header(self):
|
181 |
self.headers = {
|
182 |
"Content-Type": "application/json",
|
183 |
+
"Authorization": f"Bearer {sensitive_id}",
|
184 |
}
|
185 |
|
186 |
+
|
187 |
def _get_billing_data(self, billing_url):
|
188 |
with retrieve_proxy():
|
189 |
response = requests.get(
|
|
|
562 |
try:
|
563 |
if model_type == ModelType.OpenAI:
|
564 |
logging.info(f"正在加载OpenAI模型: {model_name}")
|
565 |
+
access_key = os.environ.get("OPENAI_API_KEY", access_key)
|
566 |
model = OpenAIClient(
|
567 |
model_name=model_name,
|
568 |
api_key=access_key,
|
|
|
613 |
elif model_type == ModelType.ChuanhuAgent:
|
614 |
from .ChuanhuAgent import ChuanhuAgent_Client
|
615 |
model = ChuanhuAgent_Client(model_name, access_key, user_name=user_name)
|
616 |
+
elif model_type == ModelType.GooglePaLM:
|
617 |
+
from .Google_PaLM import Google_PaLM_Client
|
618 |
+
access_key = os.environ.get("GOOGLE_PALM_API_KEY", access_key)
|
619 |
+
model = Google_PaLM_Client(model_name, access_key, user_name=user_name)
|
620 |
+
elif model_type == ModelType.LangchainChat:
|
621 |
+
from .azure import Azure_OpenAI_Client
|
622 |
+
model = Azure_OpenAI_Client(model_name, user_name=user_name)
|
623 |
elif model_type == ModelType.Unknown:
|
624 |
raise ValueError(f"未知模型: {model_name}")
|
625 |
logging.info(msg)
|
626 |
except Exception as e:
|
627 |
+
import traceback
|
628 |
+
traceback.print_exc()
|
629 |
msg = f"{STANDARD_ERROR_MSG}: {e}"
|
630 |
+
presudo_key = hide_middle_chars(access_key)
|
631 |
if dont_change_lora_selector:
|
632 |
+
return model, msg, chatbot, gr.update(), access_key, presudo_key
|
633 |
else:
|
634 |
+
return model, msg, chatbot, gr.Dropdown.update(choices=lora_choices, visible=lora_selector_visibility), access_key, presudo_key
|
635 |
|
636 |
|
637 |
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,7 +60,9 @@ 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",
|
@@ -72,7 +74,9 @@ ONLINE_MODELS = [
|
|
72 |
LOCAL_MODELS = [
|
73 |
"chatglm-6b",
|
74 |
"chatglm-6b-int4",
|
75 |
-
"chatglm-6b-int4-
|
|
|
|
|
76 |
"StableLM",
|
77 |
"MOSS",
|
78 |
"llama-7b-hf",
|
@@ -121,6 +125,7 @@ REPLY_LANGUAGES = [
|
|
121 |
"Español",
|
122 |
"Français",
|
123 |
"Deutsch",
|
|
|
124 |
"跟随问题语言(不稳定)"
|
125 |
]
|
126 |
|
@@ -170,6 +175,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 +229,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",
|
|
|
74 |
LOCAL_MODELS = [
|
75 |
"chatglm-6b",
|
76 |
"chatglm-6b-int4",
|
77 |
+
"chatglm-6b-int4-ge",
|
78 |
+
"chatglm2-6b",
|
79 |
+
"chatglm2-6b-int4",
|
80 |
"StableLM",
|
81 |
"MOSS",
|
82 |
"llama-7b-hf",
|
|
|
125 |
"Español",
|
126 |
"Français",
|
127 |
"Deutsch",
|
128 |
+
"한국어",
|
129 |
"跟随问题语言(不稳定)"
|
130 |
]
|
131 |
|
|
|
175 |
CONCISE SUMMARY IN 中文:"""
|
176 |
|
177 |
ALREADY_CONVERTED_MARK = "<!-- ALREADY CONVERTED BY PARSER. -->"
|
178 |
+
START_OF_OUTPUT_MARK = "<!-- SOO IN MESSAGE -->"
|
179 |
+
END_OF_OUTPUT_MARK = "<!-- EOO IN MESSAGE -->"
|
180 |
|
181 |
small_and_beautiful_theme = gr.themes.Soft(
|
182 |
primary_hue=gr.themes.Color(
|
|
|
229 |
# button_primary_background_fill_hover="*primary_400",
|
230 |
# button_primary_border_color="*primary_500",
|
231 |
button_primary_border_color_dark="*primary_600",
|
232 |
+
button_primary_text_color="white",
|
233 |
button_primary_text_color_dark="white",
|
234 |
button_secondary_background_fill="*neutral_100",
|
235 |
button_secondary_background_fill_hover="*neutral_50",
|
modules/repo.py
ADDED
@@ -0,0 +1,215 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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, 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"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 background_update():
|
162 |
+
# {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")
|
163 |
+
try:
|
164 |
+
latest_release = get_latest_release()
|
165 |
+
latest_release_tag = latest_release["tag"]
|
166 |
+
latest_release_hash = get_tag_commit_hash(latest_release_tag)
|
167 |
+
need_pip = latest_release["need_pip"]
|
168 |
+
|
169 |
+
current_branch = get_current_branch()
|
170 |
+
updater_branch = f'tmp_{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}'
|
171 |
+
track_repo = "https://github.com/GaiZhenbiao/ChuanhuChatGPT.git"
|
172 |
+
try:
|
173 |
+
try:
|
174 |
+
run(f"{git} fetch {track_repo}", desc="Fetching from github...", live=False)
|
175 |
+
except Exception:
|
176 |
+
logging.error(f"Update failed in fetching")
|
177 |
+
return "failed"
|
178 |
+
|
179 |
+
run(f'{git} stash save -a "updater-tmp"')
|
180 |
+
|
181 |
+
run(f"{git} checkout -b {updater_branch}", live=False)
|
182 |
+
run(f"{git} reset --hard FETCH_HEAD", live=False)
|
183 |
+
run(f"{git} reset --hard {latest_release_hash}", desc=f'Checking out {latest_release_tag}...')
|
184 |
+
run(f"{git} checkout {current_branch}", live=False)
|
185 |
+
|
186 |
+
try:
|
187 |
+
run(f"{git} merge {updater_branch} -q", desc="Trying to apply latest update...")
|
188 |
+
except Exception:
|
189 |
+
logging.error(f"Update failed in merging")
|
190 |
+
try:
|
191 |
+
run(f"{git} merge --abort", desc="Canceling update...")
|
192 |
+
run(f"{git} reset --hard {current_branch}", live=False)
|
193 |
+
run(f"{git} stash pop", live=False)
|
194 |
+
run(f"{git} branch -D -f {updater_branch}", live=False)
|
195 |
+
logging.error(f"Update failed, but your file was safely reset to the state before the update.")
|
196 |
+
return "failed"
|
197 |
+
except Exception as e:
|
198 |
+
logging.error(f"!!!Update failed in resetting, try to reset your files manually.")
|
199 |
+
return "failed"
|
200 |
+
|
201 |
+
run(f"{git} stash pop", live=False)
|
202 |
+
run(f"{git} branch -D -f {updater_branch}", live=False)
|
203 |
+
except Exception as e:
|
204 |
+
logging.error(f"Update failed: {e}")
|
205 |
+
return "failed"
|
206 |
+
if need_pip:
|
207 |
+
try:
|
208 |
+
run_pip(f"install -r requirements.txt", "requirements")
|
209 |
+
except Exception:
|
210 |
+
logging.error(f"Update failed in pip install")
|
211 |
+
return "failed"
|
212 |
+
return "success"
|
213 |
+
except Exception as e:
|
214 |
+
logging.error(f"Update failed: {e}")
|
215 |
+
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/utils.py
CHANGED
@@ -5,6 +5,7 @@ import logging
|
|
5 |
import json
|
6 |
import os
|
7 |
import datetime
|
|
|
8 |
import hashlib
|
9 |
import csv
|
10 |
import requests
|
@@ -47,6 +48,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 |
|
@@ -202,6 +206,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 +235,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 +249,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 +263,7 @@ def escape_markdown(text):
|
|
236 |
Escape Markdown special characters to HTML-safe equivalents.
|
237 |
"""
|
238 |
escape_chars = {
|
239 |
-
' ': ' ',
|
240 |
'_': '_',
|
241 |
'*': '*',
|
242 |
'[': '[',
|
@@ -253,8 +280,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 +539,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 +649,12 @@ def get_history_filepath(username):
|
|
654 |
|
655 |
latest_file = os.path.join(dirname, latest_file)
|
656 |
return latest_file
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
import json
|
6 |
import os
|
7 |
import datetime
|
8 |
+
from datetime import timezone
|
9 |
import hashlib
|
10 |
import csv
|
11 |
import requests
|
|
|
48 |
def load_chat_history(current_model, *args):
|
49 |
return current_model.load_chat_history(*args)
|
50 |
|
51 |
+
def delete_chat_history(current_model, *args):
|
52 |
+
return current_model.delete_chat_history(*args)
|
53 |
+
|
54 |
def interrupt(current_model, *args):
|
55 |
return current_model.interrupt(*args)
|
56 |
|
|
|
206 |
output += ALREADY_CONVERTED_MARK
|
207 |
return output
|
208 |
|
209 |
+
|
210 |
+
def clip_rawtext(chat_message, need_escape=True):
|
211 |
+
# first, clip hr line
|
212 |
+
hr_pattern = r'\n\n<hr class="append-display no-in-raw" />(.*?)'
|
213 |
+
hr_match = re.search(hr_pattern, chat_message, re.DOTALL)
|
214 |
+
message_clipped = chat_message[:hr_match.start()] if hr_match else chat_message
|
215 |
+
# second, avoid agent-prefix being escaped
|
216 |
+
agent_prefix_pattern = r'<!-- S O PREFIX --><p class="agent-prefix">(.*?)<\/p><!-- E O PREFIX -->'
|
217 |
+
agent_matches = re.findall(agent_prefix_pattern, message_clipped)
|
218 |
+
final_message = ""
|
219 |
+
if agent_matches:
|
220 |
+
agent_parts = re.split(agent_prefix_pattern, message_clipped)
|
221 |
+
for i, part in enumerate(agent_parts):
|
222 |
+
if i % 2 == 0:
|
223 |
+
final_message += escape_markdown(part) if need_escape else part
|
224 |
+
else:
|
225 |
+
final_message += f'<!-- S O PREFIX --><p class="agent-prefix">{part}</p><!-- E O PREFIX -->'
|
226 |
+
else:
|
227 |
+
final_message = escape_markdown(message_clipped) if need_escape else message_clipped
|
228 |
+
return final_message
|
229 |
+
|
230 |
+
|
231 |
def convert_bot_before_marked(chat_message):
|
232 |
"""
|
233 |
注意不能给输出加缩进, 否则会被marked解析成代码块
|
|
|
235 |
if '<div class="md-message">' in chat_message:
|
236 |
return chat_message
|
237 |
else:
|
238 |
+
raw = f'<div class="raw-message hideM"><pre>{clip_rawtext(chat_message)}</pre></div>'
|
239 |
+
# 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}'
|
240 |
+
|
241 |
code_block_pattern = re.compile(r"```(.*?)(?:```|$)", re.DOTALL)
|
242 |
code_blocks = code_block_pattern.findall(chat_message)
|
243 |
non_code_parts = code_block_pattern.split(chat_message)[::2]
|
244 |
+
result = []
|
|
|
|
|
245 |
for non_code, code in zip(non_code_parts, code_blocks + [""]):
|
246 |
if non_code.strip():
|
247 |
result.append(non_code)
|
|
|
249 |
code = f"\n```{code}\n```"
|
250 |
result.append(code)
|
251 |
result = "".join(result)
|
252 |
+
md = f'<div class="md-message">\n\n{result}\n</div>'
|
253 |
return raw + md
|
254 |
|
255 |
def convert_user_before_marked(chat_message):
|
|
|
263 |
Escape Markdown special characters to HTML-safe equivalents.
|
264 |
"""
|
265 |
escape_chars = {
|
266 |
+
# ' ': ' ',
|
267 |
'_': '_',
|
268 |
'*': '*',
|
269 |
'[': '[',
|
|
|
280 |
'`': '`',
|
281 |
'>': '>',
|
282 |
'<': '<',
|
283 |
+
'|': '|',
|
284 |
+
'$': '$',
|
285 |
+
':': ':',
|
286 |
+
'\n': '<br>',
|
287 |
}
|
288 |
+
text = text.replace(' ', ' ')
|
289 |
return ''.join(escape_chars.get(c, c) for c in text)
|
290 |
|
291 |
|
|
|
539 |
)
|
540 |
|
541 |
|
542 |
+
def update_chuanhu():
|
543 |
+
from .repo import background_update
|
544 |
|
545 |
+
print("Trying to update...")
|
546 |
+
update_status = background_update()
|
547 |
+
if update_status == "success":
|
548 |
+
print("Successfully updated, restart needed")
|
549 |
+
status = '<span id="update-status" class="hideK">success</span>'
|
550 |
+
return gr.Markdown.update(value=i18n("更新成功,请重启本程序")+status)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
551 |
else:
|
552 |
+
status = '<span id="update-status" class="hideK">failure</span>'
|
553 |
+
return gr.Markdown.update(value=i18n("更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)")+status)
|
554 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
555 |
|
556 |
def add_source_numbers(lst, source_name = "Source", use_source = True):
|
557 |
if use_source:
|
|
|
649 |
|
650 |
latest_file = os.path.join(dirname, latest_file)
|
651 |
return latest_file
|
652 |
+
|
653 |
+
def beautify_err_msg(err_msg):
|
654 |
+
if "insufficient_quota" in err_msg:
|
655 |
+
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)")
|
656 |
+
if "The model: gpt-4 does not exist" in err_msg:
|
657 |
+
return i18n("你没有权限访问 GPT4,[进一步了解](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)")
|
658 |
+
if "Resource not found" in err_msg:
|
659 |
+
return i18n("请查看 config_example.json,配置 Azure OpenAI")
|
660 |
+
return err_msg
|
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
|
requirements.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
-
gradio==3.
|
2 |
-
gradio_client==0.
|
3 |
pypinyin
|
4 |
tiktoken
|
5 |
socksio
|
@@ -16,11 +16,12 @@ commentjson
|
|
16 |
openpyxl
|
17 |
pandoc
|
18 |
wolframalpha
|
19 |
-
faiss-cpu
|
20 |
duckduckgo-search
|
21 |
arxiv
|
22 |
wikipedia
|
23 |
google.generativeai
|
24 |
openai
|
25 |
unstructured
|
|
|
26 |
tabulate
|
|
|
1 |
+
gradio==3.40.0
|
2 |
+
gradio_client==0.4.0
|
3 |
pypinyin
|
4 |
tiktoken
|
5 |
socksio
|
|
|
16 |
openpyxl
|
17 |
pandoc
|
18 |
wolframalpha
|
19 |
+
faiss-cpu==1.7.4
|
20 |
duckduckgo-search
|
21 |
arxiv
|
22 |
wikipedia
|
23 |
google.generativeai
|
24 |
openai
|
25 |
unstructured
|
26 |
+
google-api-python-client
|
27 |
tabulate
|
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
|
web_assets/favicon.ico
ADDED
web_assets/html/appearance_switcher.html
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="switch-checkbox" id="apSwitch">
|
2 |
+
<label class="apSwitch">
|
3 |
+
<input type="checkbox" id="apSwitch-checkbox" data-testid="checkbox" />
|
4 |
+
<span class="apSwitch-span">{label}</span>
|
5 |
+
</label>
|
6 |
+
</div>
|
web_assets/html/billing_info.html
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<b>{label}</b>
|
2 |
+
<div class="progress-bar">
|
3 |
+
<div class="progress" style="width: {usage_percent}%;">
|
4 |
+
<span class="progress-text">{usage_percent}%</span>
|
5 |
+
</div>
|
6 |
+
</div>
|
7 |
+
<div style="display: flex; justify-content: space-between;">
|
8 |
+
<span>${rounded_usage}</span><span>${usage_limit}</span>
|
9 |
+
</div>
|
web_assets/html/footer.html
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
<div class="versions">{versions}</div>
|
web_assets/html/update.html
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div id="toast-update">
|
2 |
+
<div id="check-chuanhu-update">
|
3 |
+
<p style="display:none">
|
4 |
+
<span id="current-version">{current_version}</span>
|
5 |
+
<span id="version-time">{version_time}</span>
|
6 |
+
</p>
|
7 |
+
<p id="version-info-title">
|
8 |
+
Latest Version: <a href="https://github.com/gaizhenbiao/chuanhuchatgpt/releases/latest" target="_blank"
|
9 |
+
id="latest-version-title" style="text-decoration: none;">getting latest version...</a>
|
10 |
+
</p>
|
11 |
+
<p id="updating-info" class="hideK">
|
12 |
+
Getting update...
|
13 |
+
</p>
|
14 |
+
<div id="updating-spinner"></div>
|
15 |
+
<div id="release-note-wrap">
|
16 |
+
<div class="release-note-content" id="release-note-content">
|
17 |
+
Getting Release Note...
|
18 |
+
</div>
|
19 |
+
</div>
|
20 |
+
<div id="goto-update-btn" class="btn-update-group">
|
21 |
+
<button class="btn-update lg secondary svelte-cmf5ev" id="cancel-button" onclick="cancelUpdate()">{cancel_btn}</button>
|
22 |
+
<button class="btn-update lg primary svelte-cmf5ev" id="update-button" onclick="bgUpdateChuanhu()">{update_btn}</button>
|
23 |
+
</div>
|
24 |
+
<div id="close-update-btn" class="btn-update-group hideK">
|
25 |
+
<button class="btn-update lg secondary svelte-cmf5ev" id="seenew-button" onclick="getUpdateInfo()">{seenew_btn}</button>
|
26 |
+
<button class="btn-update lg primary svelte-cmf5ev" id="ok-button" onclick="cancelUpdate()">{ok_btn}</button>
|
27 |
+
</div>
|
28 |
+
</div>
|
29 |
+
</div>
|
web_assets/javascript/ChuanhuChat.js
ADDED
@@ -0,0 +1,317 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
// ChuanhuChat core javascript
|
3 |
+
|
4 |
+
const MAX_HISTORY_LENGTH = 32;
|
5 |
+
|
6 |
+
var key_down_history = [];
|
7 |
+
var currentIndex = -1;
|
8 |
+
|
9 |
+
var gradioContainer = null;
|
10 |
+
var user_input_ta = null;
|
11 |
+
var user_input_tb = null;
|
12 |
+
var userInfoDiv = null;
|
13 |
+
var appTitleDiv = null;
|
14 |
+
var chatbot = null;
|
15 |
+
var chatbotWrap = null;
|
16 |
+
var apSwitch = null;
|
17 |
+
var messageBotDivs = null;
|
18 |
+
var loginUserForm = null;
|
19 |
+
var logginUser = null;
|
20 |
+
var updateToast = null;
|
21 |
+
var sendBtn = null;
|
22 |
+
var cancelBtn = null;
|
23 |
+
var sliders = null;
|
24 |
+
var updateChuanhuBtn = null;
|
25 |
+
var statusDisplay = null;
|
26 |
+
|
27 |
+
|
28 |
+
var isInIframe = (window.self !== window.top);
|
29 |
+
var currentTime = new Date().getTime();
|
30 |
+
var initialized = false;
|
31 |
+
|
32 |
+
// gradio 页面加载好了么??? 我能动你的元素了么??
|
33 |
+
function gradioLoaded(mutations) {
|
34 |
+
for (var i = 0; i < mutations.length; i++) {
|
35 |
+
if (mutations[i].addedNodes.length) {
|
36 |
+
if (initialized) {
|
37 |
+
observer.disconnect(); // 停止监听
|
38 |
+
return;
|
39 |
+
}
|
40 |
+
initialize();
|
41 |
+
}
|
42 |
+
}
|
43 |
+
}
|
44 |
+
|
45 |
+
function initialize() {
|
46 |
+
var needInit = {gradioContainer, apSwitch, user_input_tb, userInfoDiv, appTitleDiv, chatbot, chatbotWrap, statusDisplay, sliders, updateChuanhuBtn};
|
47 |
+
initialized = true;
|
48 |
+
|
49 |
+
loginUserForm = gradioApp().querySelector(".gradio-container > .main > .wrap > .panel > .form")
|
50 |
+
gradioContainer = gradioApp().querySelector(".gradio-container");
|
51 |
+
user_input_tb = gradioApp().getElementById('user-input-tb');
|
52 |
+
userInfoDiv = gradioApp().getElementById("user-info");
|
53 |
+
appTitleDiv = gradioApp().getElementById("app-title");
|
54 |
+
chatbot = gradioApp().querySelector('#chuanhu-chatbot');
|
55 |
+
chatbotWrap = gradioApp().querySelector('#chuanhu-chatbot > .wrapper > .wrap');
|
56 |
+
apSwitch = gradioApp().querySelector('.apSwitch input[type="checkbox"]');
|
57 |
+
updateToast = gradioApp().querySelector("#toast-update");
|
58 |
+
sendBtn = gradioApp().getElementById("submit-btn");
|
59 |
+
cancelBtn = gradioApp().getElementById("cancel-btn");
|
60 |
+
sliders = gradioApp().querySelectorAll('input[type="range"]');
|
61 |
+
updateChuanhuBtn = gradioApp().getElementById("update-chuanhu-btn");
|
62 |
+
statusDisplay = gradioApp().querySelector('#status-display');
|
63 |
+
|
64 |
+
if (loginUserForm) {
|
65 |
+
localStorage.setItem("userLogged", true);
|
66 |
+
userLogged = true;
|
67 |
+
}
|
68 |
+
|
69 |
+
for (let elem in needInit) {
|
70 |
+
if (needInit[elem] == null) {
|
71 |
+
initialized = false;
|
72 |
+
return;
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
if (initialized) {
|
77 |
+
adjustDarkMode();
|
78 |
+
selectHistory();
|
79 |
+
setTimeout(showOrHideUserInfo(), 2000);
|
80 |
+
setChatbotHeight();
|
81 |
+
setChatbotScroll();
|
82 |
+
setSlider();
|
83 |
+
if (!historyLoaded) loadHistoryHtml();
|
84 |
+
if (!usernameGotten) getUserInfo();
|
85 |
+
mObserver.observe(chatbotWrap, { attributes: true, childList: true, subtree: true, characterData: true });
|
86 |
+
submitObserver.observe(cancelBtn, { attributes: true, characterData: true });
|
87 |
+
|
88 |
+
const lastCheckTime = localStorage.getItem('lastCheckTime') || 0;
|
89 |
+
const longTimeNoCheck = currentTime - lastCheckTime > 3 * 24 * 60 * 60 * 1000;
|
90 |
+
if (longTimeNoCheck && !updateInfoGotten && !isLatestVersion || isLatestVersion && !updateInfoGotten) {
|
91 |
+
updateLatestVersion();
|
92 |
+
}
|
93 |
+
}
|
94 |
+
}
|
95 |
+
|
96 |
+
function gradioApp() {
|
97 |
+
const elems = document.getElementsByTagName('gradio-app');
|
98 |
+
const elem = elems.length == 0 ? document : elems[0];
|
99 |
+
|
100 |
+
if (elem !== document) {
|
101 |
+
elem.getElementById = function(id) {
|
102 |
+
return document.getElementById(id);
|
103 |
+
};
|
104 |
+
}
|
105 |
+
return elem.shadowRoot ? elem.shadowRoot : elem;
|
106 |
+
}
|
107 |
+
|
108 |
+
function showConfirmationDialog(a, file, c) {
|
109 |
+
if (file != "") {
|
110 |
+
var result = confirm(i18n(deleteConfirm_i18n_pref) + file + i18n(deleteConfirm_i18n_suff));
|
111 |
+
if (result) {
|
112 |
+
return [a, file, c];
|
113 |
+
}
|
114 |
+
}
|
115 |
+
return [a, "CANCELED", c];
|
116 |
+
}
|
117 |
+
|
118 |
+
function selectHistory() {
|
119 |
+
user_input_ta = user_input_tb.querySelector("textarea");
|
120 |
+
if (user_input_ta) {
|
121 |
+
disableSendBtn();
|
122 |
+
// 在 textarea 上监听 keydown 事件
|
123 |
+
user_input_ta.addEventListener("keydown", function (event) {
|
124 |
+
var value = user_input_ta.value.trim();
|
125 |
+
// 判断按下的是否为方向键
|
126 |
+
if (event.code === 'ArrowUp' || event.code === 'ArrowDown') {
|
127 |
+
// 如果按下的是方向键,且输入框中有内容,且历史记录中没有该内容,则不执行操作
|
128 |
+
if (value && key_down_history.indexOf(value) === -1)
|
129 |
+
return;
|
130 |
+
// 对于需要响应的动作,阻止默认行为。
|
131 |
+
event.preventDefault();
|
132 |
+
var length = key_down_history.length;
|
133 |
+
if (length === 0) {
|
134 |
+
currentIndex = -1; // 如果历史记录为空,直接将当前选中的记录重置
|
135 |
+
return;
|
136 |
+
}
|
137 |
+
if (currentIndex === -1) {
|
138 |
+
currentIndex = length;
|
139 |
+
}
|
140 |
+
if (event.code === 'ArrowUp' && currentIndex > 0) {
|
141 |
+
currentIndex--;
|
142 |
+
user_input_ta.value = key_down_history[currentIndex];
|
143 |
+
} else if (event.code === 'ArrowDown' && currentIndex < length - 1) {
|
144 |
+
currentIndex++;
|
145 |
+
user_input_ta.value = key_down_history[currentIndex];
|
146 |
+
}
|
147 |
+
user_input_ta.selectionStart = user_input_ta.value.length;
|
148 |
+
user_input_ta.selectionEnd = user_input_ta.value.length;
|
149 |
+
const input_event = new InputEvent("input", { bubbles: true, cancelable: true });
|
150 |
+
user_input_ta.dispatchEvent(input_event);
|
151 |
+
} else if (event.code === "Enter") {
|
152 |
+
if (value) {
|
153 |
+
currentIndex = -1;
|
154 |
+
if (key_down_history.indexOf(value) === -1) {
|
155 |
+
key_down_history.push(value);
|
156 |
+
if (key_down_history.length > MAX_HISTORY_LENGTH) {
|
157 |
+
key_down_history.shift();
|
158 |
+
}
|
159 |
+
}
|
160 |
+
}
|
161 |
+
}
|
162 |
+
});
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
+
function disableSendBtn() {
|
167 |
+
sendBtn.disabled = user_input_ta.value.trim() === '';
|
168 |
+
user_input_ta.addEventListener('input', () => {
|
169 |
+
sendBtn.disabled = user_input_ta.value.trim() === '';
|
170 |
+
});
|
171 |
+
}
|
172 |
+
|
173 |
+
function adjustDarkMode() {
|
174 |
+
function toggleDarkMode(isEnabled) {
|
175 |
+
if (isEnabled) {
|
176 |
+
document.body.classList.add("dark");
|
177 |
+
document.body.style.setProperty("background-color", "var(--neutral-950)", "important");
|
178 |
+
} else {
|
179 |
+
document.body.classList.remove("dark");
|
180 |
+
document.body.style.backgroundColor = "";
|
181 |
+
}
|
182 |
+
}
|
183 |
+
|
184 |
+
const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
185 |
+
apSwitch.checked = darkModeQuery.matches;
|
186 |
+
toggleDarkMode(darkModeQuery.matches);
|
187 |
+
darkModeQuery.addEventListener("change", (e) => {
|
188 |
+
apSwitch.checked = e.matches;
|
189 |
+
toggleDarkMode(e.matches);
|
190 |
+
});
|
191 |
+
apSwitch.addEventListener("change", (e) => {
|
192 |
+
toggleDarkMode(e.target.checked);
|
193 |
+
});
|
194 |
+
}
|
195 |
+
|
196 |
+
function setChatbotHeight() {
|
197 |
+
const screenWidth = window.innerWidth;
|
198 |
+
const statusDisplay = document.querySelector('#status-display');
|
199 |
+
const statusDisplayHeight = statusDisplay ? statusDisplay.offsetHeight : 0;
|
200 |
+
const vh = window.innerHeight * 0.01;
|
201 |
+
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
202 |
+
if (isInIframe) {
|
203 |
+
chatbot.style.height = `700px`;
|
204 |
+
chatbotWrap.style.maxHeight = `calc(700px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`
|
205 |
+
} else {
|
206 |
+
if (screenWidth <= 320) {
|
207 |
+
chatbot.style.height = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 150}px)`;
|
208 |
+
chatbotWrap.style.maxHeight = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 150}px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`;
|
209 |
+
} else if (screenWidth <= 499) {
|
210 |
+
chatbot.style.height = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 100}px)`;
|
211 |
+
chatbotWrap.style.maxHeight = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 100}px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`;
|
212 |
+
} else {
|
213 |
+
chatbot.style.height = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 160}px)`;
|
214 |
+
chatbotWrap.style.maxHeight = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 160}px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`;
|
215 |
+
}
|
216 |
+
}
|
217 |
+
}
|
218 |
+
function setChatbotScroll() {
|
219 |
+
var scrollHeight = chatbotWrap.scrollHeight;
|
220 |
+
chatbotWrap.scrollTo(0,scrollHeight)
|
221 |
+
}
|
222 |
+
|
223 |
+
|
224 |
+
let timeoutId;
|
225 |
+
let isThrottled = false;
|
226 |
+
// 监听chatWrap元素的变化,为 bot 消息添加复制按钮。
|
227 |
+
var mObserver = new MutationObserver(function (mutationsList) {
|
228 |
+
for (const mmutation of mutationsList) {
|
229 |
+
if (mmutation.type === 'childList') {
|
230 |
+
for (var node of mmutation.addedNodes) {
|
231 |
+
if (node.nodeType === 1 && node.classList.contains('message')) {
|
232 |
+
saveHistoryHtml();
|
233 |
+
disableSendBtn();
|
234 |
+
document.querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
|
235 |
+
}
|
236 |
+
}
|
237 |
+
for (var node of mmutation.removedNodes) {
|
238 |
+
if (node.nodeType === 1 && node.classList.contains('message')) {
|
239 |
+
saveHistoryHtml();
|
240 |
+
disableSendBtn();
|
241 |
+
document.querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
|
242 |
+
}
|
243 |
+
}
|
244 |
+
} else if (mmutation.type === 'attributes') {
|
245 |
+
if (isThrottled) break; // 为了防止重复不断疯狂渲染,加上等待_(:з」∠)_
|
246 |
+
isThrottled = true;
|
247 |
+
clearTimeout(timeoutId);
|
248 |
+
timeoutId = setTimeout(() => {
|
249 |
+
isThrottled = false;
|
250 |
+
document.querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
|
251 |
+
saveHistoryHtml();
|
252 |
+
disableSendBtn();
|
253 |
+
}, 1500);
|
254 |
+
}
|
255 |
+
}
|
256 |
+
});
|
257 |
+
|
258 |
+
var submitObserver = new MutationObserver(function (mutationsList) {
|
259 |
+
document.querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
|
260 |
+
saveHistoryHtml();
|
261 |
+
});
|
262 |
+
|
263 |
+
// 监视页面内部 DOM 变动
|
264 |
+
var observer = new MutationObserver(function (mutations) {
|
265 |
+
gradioLoaded(mutations);
|
266 |
+
});
|
267 |
+
|
268 |
+
// 监视页面变化
|
269 |
+
window.addEventListener("DOMContentLoaded", function () {
|
270 |
+
const ga = document.getElementsByTagName("gradio-app");
|
271 |
+
observer.observe(ga[0], { childList: true, subtree: true });
|
272 |
+
isInIframe = (window.self !== window.top);
|
273 |
+
historyLoaded = false;
|
274 |
+
});
|
275 |
+
window.addEventListener('resize', setChatbotHeight);
|
276 |
+
window.addEventListener('scroll', function(){setChatbotHeight(); setUpdateWindowHeight();});
|
277 |
+
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", adjustDarkMode);
|
278 |
+
|
279 |
+
// console suprise
|
280 |
+
var styleTitle1 = `
|
281 |
+
font-size: 16px;
|
282 |
+
font-family: ui-monospace, monospace;
|
283 |
+
color: #06AE56;
|
284 |
+
`
|
285 |
+
var styleDesc1 = `
|
286 |
+
font-size: 12px;
|
287 |
+
font-family: ui-monospace, monospace;
|
288 |
+
`
|
289 |
+
function makeML(str) {
|
290 |
+
let l = new String(str)
|
291 |
+
l = l.substring(l.indexOf("/*") + 3, l.lastIndexOf("*/"))
|
292 |
+
return l
|
293 |
+
}
|
294 |
+
let ChuanhuInfo = function () {
|
295 |
+
/*
|
296 |
+
________ __ ________ __
|
297 |
+
/ ____/ /_ __ ______ _____ / /_ __ __ / ____/ /_ ____ _/ /_
|
298 |
+
/ / / __ \/ / / / __ `/ __ \/ __ \/ / / / / / / __ \/ __ `/ __/
|
299 |
+
/ /___/ / / / /_/ / /_/ / / / / / / / /_/ / / /___/ / / / /_/ / /_
|
300 |
+
\____/_/ /_/\__,_/\__,_/_/ /_/_/ /_/\__,_/ \____/_/ /_/\__,_/\__/
|
301 |
+
|
302 |
+
川虎Chat (Chuanhu Chat) - GUI for ChatGPT API and many LLMs
|
303 |
+
*/
|
304 |
+
}
|
305 |
+
let description = `
|
306 |
+
© 2023 Chuanhu, MZhao, Keldos
|
307 |
+
GitHub repository: [https://github.com/GaiZhenbiao/ChuanhuChatGPT]\n
|
308 |
+
Enjoy our project!\n
|
309 |
+
`
|
310 |
+
console.log(`%c${makeML(ChuanhuInfo)}`,styleTitle1)
|
311 |
+
console.log(`%c${description}`, styleDesc1)
|
312 |
+
|
313 |
+
// button svg code
|
314 |
+
const copyIcon = '<span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg></span>';
|
315 |
+
const copiedIcon = '<span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><polyline points="20 6 9 17 4 12"></polyline></svg></span>';
|
316 |
+
const mdIcon = '<span><svg stroke="currentColor" fill="none" stroke-width="1" viewBox="0 0 14 18" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><g transform-origin="center" transform="scale(0.85)"><path d="M1.5,0 L12.5,0 C13.3284271,-1.52179594e-16 14,0.671572875 14,1.5 L14,16.5 C14,17.3284271 13.3284271,18 12.5,18 L1.5,18 C0.671572875,18 1.01453063e-16,17.3284271 0,16.5 L0,1.5 C-1.01453063e-16,0.671572875 0.671572875,1.52179594e-16 1.5,0 Z" stroke-width="1.8"></path><line x1="3.5" y1="3.5" x2="10.5" y2="3.5"></line><line x1="3.5" y1="6.5" x2="8" y2="6.5"></line></g><path d="M4,9 L10,9 C10.5522847,9 11,9.44771525 11,10 L11,13.5 C11,14.0522847 10.5522847,14.5 10,14.5 L4,14.5 C3.44771525,14.5 3,14.0522847 3,13.5 L3,10 C3,9.44771525 3.44771525,9 4,9 Z" stroke="none" fill="currentColor"></path></svg></span>';
|
317 |
+
const rawIcon = '<span><svg stroke="currentColor" fill="none" stroke-width="1.8" viewBox="0 0 18 14" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><g transform-origin="center" transform="scale(0.85)"><polyline points="4 3 0 7 4 11"></polyline><polyline points="14 3 18 7 14 11"></polyline><line x1="12" y1="0" x2="6" y2="14"></line></g></svg></span>';
|
web_assets/javascript/chat-history.js
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
var historyLoaded = false;
|
3 |
+
var loadhistorytime = 0; // for debugging
|
4 |
+
|
5 |
+
|
6 |
+
function saveHistoryHtml() {
|
7 |
+
var historyHtml = document.querySelector('#chuanhu-chatbot>.wrapper>.wrap');
|
8 |
+
if (!historyHtml) return; // no history, do nothing
|
9 |
+
localStorage.setItem('chatHistory', historyHtml.innerHTML);
|
10 |
+
// console.log("History Saved")
|
11 |
+
historyLoaded = false;
|
12 |
+
}
|
13 |
+
|
14 |
+
function loadHistoryHtml() {
|
15 |
+
var historyHtml = localStorage.getItem('chatHistory');
|
16 |
+
const tempDiv = document.createElement('div');
|
17 |
+
tempDiv.innerHTML = historyHtml;
|
18 |
+
if (!historyHtml || tempDiv.innerText.trim() === "") {
|
19 |
+
historyLoaded = true;
|
20 |
+
return; // no history, do nothing
|
21 |
+
}
|
22 |
+
userLogged = localStorage.getItem('userLogged');
|
23 |
+
if (userLogged){
|
24 |
+
historyLoaded = true;
|
25 |
+
return; // logged in, do nothing
|
26 |
+
}
|
27 |
+
if (!historyLoaded) {
|
28 |
+
var buttons = tempDiv.querySelectorAll('button.chuanhu-btn');
|
29 |
+
var gradioCopyButtons = tempDiv.querySelectorAll('button.copy_code_button');
|
30 |
+
for (var i = 0; i < buttons.length; i++) {
|
31 |
+
buttons[i].parentNode.removeChild(buttons[i]);
|
32 |
+
}
|
33 |
+
for (var i = 0; i < gradioCopyButtons.length; i++) {
|
34 |
+
gradioCopyButtons[i].parentNode.removeChild(gradioCopyButtons[i]);
|
35 |
+
}
|
36 |
+
var fakeHistory = document.createElement('div');
|
37 |
+
fakeHistory.classList.add('history-message');
|
38 |
+
fakeHistory.innerHTML = tempDiv.innerHTML;
|
39 |
+
const forViewStyle = document.createElement('style');
|
40 |
+
forViewStyle.innerHTML = '.wrapper>.wrap>.history-message>:last-child::after { content: "' + i18n(forView_i18n) + '"!important; }';
|
41 |
+
document.head.appendChild(forViewStyle);
|
42 |
+
chatbotWrap.insertBefore(fakeHistory, chatbotWrap.firstChild);
|
43 |
+
// var fakeHistory = document.createElement('div');
|
44 |
+
// fakeHistory.classList.add('history-message');
|
45 |
+
// fakeHistory.innerHTML = historyHtml;
|
46 |
+
// chatbotWrap.insertBefore(fakeHistory, chatbotWrap.firstChild);
|
47 |
+
historyLoaded = true;
|
48 |
+
console.log("History Loaded");
|
49 |
+
loadhistorytime += 1; // for debugging
|
50 |
+
} else {
|
51 |
+
historyLoaded = false;
|
52 |
+
}
|
53 |
+
}
|
54 |
+
|
55 |
+
function clearHistoryHtml() {
|
56 |
+
localStorage.removeItem("chatHistory");
|
57 |
+
historyMessages = chatbotWrap.querySelector('.history-message');
|
58 |
+
if (historyMessages) {
|
59 |
+
chatbotWrap.removeChild(historyMessages);
|
60 |
+
console.log("History Cleared");
|
61 |
+
}
|
62 |
+
}
|
web_assets/javascript/external-scripts.js
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
|
2 |
+
// external javascript here
|
web_assets/javascript/localization.js
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
// i18n
|
3 |
+
|
4 |
+
const language = navigator.language.slice(0,2);
|
5 |
+
|
6 |
+
const forView_i18n = {
|
7 |
+
'zh': "仅供查看",
|
8 |
+
'en': "For viewing only",
|
9 |
+
'ja': "閲覧専用",
|
10 |
+
'ko': "읽기 전용",
|
11 |
+
'fr': "Pour consultation seulement",
|
12 |
+
'es': "Solo para visualización",
|
13 |
+
'sv': "Endast för visning",
|
14 |
+
};
|
15 |
+
|
16 |
+
const deleteConfirm_i18n_pref = {
|
17 |
+
'zh': "你真的要删除 ",
|
18 |
+
'en': "Are you sure you want to delete ",
|
19 |
+
'ja': "本当に ",
|
20 |
+
'ko': "정말로 ",
|
21 |
+
'sv': "Är du säker på att du vill ta bort "
|
22 |
+
};
|
23 |
+
|
24 |
+
const deleteConfirm_i18n_suff = {
|
25 |
+
'zh': " 吗?",
|
26 |
+
'en': " ?",
|
27 |
+
'ja': " を削除してもよろしいですか?",
|
28 |
+
'ko': " 을(를) 삭제하시겠습니까?",
|
29 |
+
'sv': " ?"
|
30 |
+
};
|
31 |
+
|
32 |
+
const usingLatest_i18n = {
|
33 |
+
'zh': "您使用的就是最新版!",
|
34 |
+
'en': "You are using the latest version!",
|
35 |
+
'ja': "最新バージョンを使用しています!",
|
36 |
+
'ko': "최신 버전을 사용하고 있습니다!",
|
37 |
+
'sv': "Du använder den senaste versionen!"
|
38 |
+
};
|
39 |
+
|
40 |
+
const updatingMsg_i18n = {
|
41 |
+
'zh': "正在尝试更新...",
|
42 |
+
'en': "Trying to update...",
|
43 |
+
'ja': "更新を試みています...",
|
44 |
+
'ko': "업데이트를 시도 중...",
|
45 |
+
'sv': "Försöker uppdatera..."
|
46 |
+
}
|
47 |
+
|
48 |
+
const updateSuccess_i18n = {
|
49 |
+
'zh': "更新成功,请重启本程序。",
|
50 |
+
'en': "Updated successfully, please restart this program.",
|
51 |
+
'ja': "更新が成功しました、このプログラムを再起動してください。",
|
52 |
+
'ko': "업데이트 성공, 이 프로그램을 재시작 해주세요.",
|
53 |
+
'sv': "Uppdaterat framgångsrikt, starta om programmet."
|
54 |
+
}
|
55 |
+
|
56 |
+
const updateFailure_i18n = {
|
57 |
+
'zh': '更新失败,请尝试<a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">手动更新</a>。',
|
58 |
+
'en': 'Update failed, please try <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">manually updating</a>.',
|
59 |
+
'ja': '更新に失敗しました、<a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">手動での更新</a>をお試しください。',
|
60 |
+
'ko': '업데이트 실패, <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">수동 업데이트</a>를 시도하십시오.',
|
61 |
+
'sv': 'Uppdateringen misslyckades, prova att <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blank">uppdatera manuellt</a>.'
|
62 |
+
}
|
63 |
+
|
64 |
+
|
65 |
+
function i18n(msg) {
|
66 |
+
return msg.hasOwnProperty(language) ? msg[language] : msg['en'];
|
67 |
+
}
|
web_assets/javascript/message-button.js
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
// 为 bot 消息添加复制与切换显示按钮
|
3 |
+
|
4 |
+
function addChuanhuButton(botElement) {
|
5 |
+
var rawMessage = botElement.querySelector('.raw-message');
|
6 |
+
var mdMessage = botElement.querySelector('.md-message');
|
7 |
+
// var gradioCopyMsgBtn = botElement.querySelector('div.icon-button>button[title="copy"]'); // 获取 gradio 的 copy button,它可以读取真正的原始 message
|
8 |
+
if (!rawMessage) {
|
9 |
+
var buttons = botElement.querySelectorAll('button.chuanhu-btn');
|
10 |
+
for (var i = 0; i < buttons.length; i++) {
|
11 |
+
buttons[i].parentNode.removeChild(buttons[i]);
|
12 |
+
}
|
13 |
+
return;
|
14 |
+
}
|
15 |
+
var oldCopyButton = null;
|
16 |
+
var oldToggleButton = null;
|
17 |
+
oldCopyButton = botElement.querySelector('button.copy-bot-btn');
|
18 |
+
oldToggleButton = botElement.querySelector('button.toggle-md-btn');
|
19 |
+
if (oldCopyButton) oldCopyButton.remove();
|
20 |
+
if (oldToggleButton) oldToggleButton.remove();
|
21 |
+
|
22 |
+
// Copy bot button
|
23 |
+
var copyButton = document.createElement('button');
|
24 |
+
copyButton.classList.add('chuanhu-btn');
|
25 |
+
copyButton.classList.add('copy-bot-btn');
|
26 |
+
copyButton.setAttribute('aria-label', 'Copy');
|
27 |
+
copyButton.innerHTML = copyIcon;
|
28 |
+
|
29 |
+
copyButton.addEventListener('click', async () => {
|
30 |
+
const textToCopy = rawMessage.innerText;
|
31 |
+
try {
|
32 |
+
if ("clipboard" in navigator) {
|
33 |
+
await navigator.clipboard.writeText(textToCopy);
|
34 |
+
copyButton.innerHTML = copiedIcon;
|
35 |
+
setTimeout(() => {
|
36 |
+
copyButton.innerHTML = copyIcon;
|
37 |
+
}, 1500);
|
38 |
+
} else {
|
39 |
+
const textArea = document.createElement("textarea");
|
40 |
+
textArea.value = textToCopy;
|
41 |
+
document.body.appendChild(textArea);
|
42 |
+
textArea.select();
|
43 |
+
try {
|
44 |
+
document.execCommand('copy');
|
45 |
+
copyButton.innerHTML = copiedIcon;
|
46 |
+
setTimeout(() => {
|
47 |
+
copyButton.innerHTML = copyIcon;
|
48 |
+
}, 1500);
|
49 |
+
} catch (error) {
|
50 |
+
console.error("Copy failed: ", error);
|
51 |
+
}
|
52 |
+
document.body.removeChild(textArea);
|
53 |
+
}
|
54 |
+
} catch (error) {
|
55 |
+
console.error("Copy failed: ", error);
|
56 |
+
}
|
57 |
+
});
|
58 |
+
botElement.appendChild(copyButton);
|
59 |
+
|
60 |
+
// Toggle button
|
61 |
+
var toggleButton = document.createElement('button');
|
62 |
+
toggleButton.classList.add('chuanhu-btn');
|
63 |
+
toggleButton.classList.add('toggle-md-btn');
|
64 |
+
toggleButton.setAttribute('aria-label', 'Toggle');
|
65 |
+
var renderMarkdown = mdMessage.classList.contains('hideM');
|
66 |
+
toggleButton.innerHTML = renderMarkdown ? mdIcon : rawIcon;
|
67 |
+
toggleButton.addEventListener('click', () => {
|
68 |
+
renderMarkdown = mdMessage.classList.contains('hideM');
|
69 |
+
if (renderMarkdown){
|
70 |
+
renderMarkdownText(botElement);
|
71 |
+
toggleButton.innerHTML=rawIcon;
|
72 |
+
} else {
|
73 |
+
removeMarkdownText(botElement);
|
74 |
+
toggleButton.innerHTML=mdIcon;
|
75 |
+
}
|
76 |
+
});
|
77 |
+
botElement.insertBefore(toggleButton, copyButton);
|
78 |
+
|
79 |
+
function renderMarkdownText(message) {
|
80 |
+
var mdDiv = message.querySelector('.md-message');
|
81 |
+
if (mdDiv) mdDiv.classList.remove('hideM');
|
82 |
+
var rawDiv = message.querySelector('.raw-message');
|
83 |
+
if (rawDiv) rawDiv.classList.add('hideM');
|
84 |
+
}
|
85 |
+
function removeMarkdownText(message) {
|
86 |
+
var rawDiv = message.querySelector('.raw-message');
|
87 |
+
if (rawDiv) {
|
88 |
+
rawPre = rawDiv.querySelector('pre');
|
89 |
+
if (rawPre) rawDiv.innerHTML = rawPre.innerHTML;
|
90 |
+
rawDiv.classList.remove('hideM');
|
91 |
+
}
|
92 |
+
var mdDiv = message.querySelector('.md-message');
|
93 |
+
if (mdDiv) mdDiv.classList.add('hideM');
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
|
web_assets/javascript/sliders.js
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
var rangeInputs = null;
|
3 |
+
var numberInputs = null;
|
4 |
+
|
5 |
+
|
6 |
+
function setSlider() {
|
7 |
+
function setSliderRange() {
|
8 |
+
var range = document.querySelectorAll('input[type="range"]');
|
9 |
+
range.forEach(range => {
|
10 |
+
range.style.backgroundSize = (range.value - range.min) / (range.max - range.min) * 100 + '% 100%';
|
11 |
+
});
|
12 |
+
}
|
13 |
+
rangeInputs = document.querySelectorAll('input[type="range"]');
|
14 |
+
numberInputs = document.querySelectorAll('input[type="number"]')
|
15 |
+
setSliderRange();
|
16 |
+
rangeInputs.forEach(rangeInput => {
|
17 |
+
rangeInput.addEventListener('input', setSliderRange);
|
18 |
+
});
|
19 |
+
numberInputs.forEach(numberInput => {
|
20 |
+
numberInput.addEventListener('input', setSliderRange);
|
21 |
+
})
|
22 |
+
}
|
web_assets/javascript/updater.js
ADDED
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
var updateInfoGotten = false;
|
3 |
+
var isLatestVersion = localStorage.getItem('isLatestVersion') || false;
|
4 |
+
|
5 |
+
|
6 |
+
var statusObserver = new MutationObserver(function (mutationsList) {
|
7 |
+
for (const mutation of mutationsList) {
|
8 |
+
if (mutation.type === 'attributes' || mutation.type === 'childList') {
|
9 |
+
if (statusDisplay.innerHTML.includes('<span id="update-status"')) {
|
10 |
+
if (getUpdateStatus() === "success") {
|
11 |
+
updatingInfoElement.innerText = i18n(updateSuccess_i18n);
|
12 |
+
noUpdateHtml();
|
13 |
+
localStorage.setItem('isLatestVersion', 'true');
|
14 |
+
isLatestVersion = true;
|
15 |
+
enableUpdateBtns();
|
16 |
+
} else if (getUpdateStatus() === "failure") {
|
17 |
+
updatingInfoElement.innerHTML = i18n(updateFailure_i18n);
|
18 |
+
disableUpdateBtn_enableCancelBtn();
|
19 |
+
} else if (getUpdateStatus() != "") {
|
20 |
+
updatingInfoElement.innerText = getUpdateStatus();
|
21 |
+
enableUpdateBtns();
|
22 |
+
}
|
23 |
+
updateStatus.parentNode.removeChild(updateStatus);
|
24 |
+
if (updateSpinner) updateSpinner.stop();
|
25 |
+
}
|
26 |
+
}
|
27 |
+
}
|
28 |
+
});
|
29 |
+
|
30 |
+
var showingUpdateInfo = false;
|
31 |
+
async function getLatestRelease() {
|
32 |
+
try {
|
33 |
+
const response = await fetch('https://api.github.com/repos/gaizhenbiao/chuanhuchatgpt/releases/latest');
|
34 |
+
if (!response.ok) {
|
35 |
+
console.log(`Error: ${response.status} - ${response.statusText}`);
|
36 |
+
updateInfoGotten = true;
|
37 |
+
return null;
|
38 |
+
}
|
39 |
+
const data = await response.json();
|
40 |
+
updateInfoGotten = true;
|
41 |
+
return data;
|
42 |
+
} catch (error) {
|
43 |
+
console.log(`Error: ${error}`);
|
44 |
+
updateInfoGotten = true;
|
45 |
+
return null;
|
46 |
+
}
|
47 |
+
}
|
48 |
+
|
49 |
+
var releaseNoteElement = document.getElementById('release-note-content');
|
50 |
+
var updatingInfoElement = document.getElementById('updating-info');
|
51 |
+
async function updateLatestVersion() {
|
52 |
+
const currentVersionElement = document.getElementById('current-version');
|
53 |
+
const reVersion = /<a[^>]*>([^<]*)<\/a>/g;
|
54 |
+
const versionMatch = reVersion.exec(currentVersionElement.innerHTML);
|
55 |
+
const currentVersion = (versionMatch && versionMatch[1].length == 8) ? versionMatch[1] : null;
|
56 |
+
const latestVersionElement = document.getElementById('latest-version-title');
|
57 |
+
const versionInfoElement = document.getElementById('version-info-title');
|
58 |
+
releaseNoteElement = document.getElementById('release-note-content');
|
59 |
+
updatingInfoElement = document.getElementById('updating-info');
|
60 |
+
|
61 |
+
const versionTime = document.getElementById('version-time').innerText;
|
62 |
+
const localVersionTime = versionTime !== "unknown" ? (new Date(versionTime)).getTime() : 0;
|
63 |
+
updateInfoGotten = true; //无论成功与否都只执行一次,否则容易api超限...
|
64 |
+
try {
|
65 |
+
const data = await getLatestRelease();
|
66 |
+
const releaseNote = data.body;
|
67 |
+
if (releaseNote) {
|
68 |
+
releaseNoteElement.innerHTML = marked.parse(releaseNote, {mangle: false, headerIds: false});
|
69 |
+
}
|
70 |
+
const latestVersion = data.tag_name;
|
71 |
+
if (currentVersion) {
|
72 |
+
if (latestVersion <= currentVersion) {
|
73 |
+
noUpdate();
|
74 |
+
} else {
|
75 |
+
latestVersionElement.textContent = latestVersion;
|
76 |
+
console.log(`New version ${latestVersion} found!`);
|
77 |
+
if (!isInIframe) openUpdateToast();
|
78 |
+
}
|
79 |
+
} else { //如果当前版本号获取失败,使用时间比较
|
80 |
+
const latestVersionTime = (new Date(data.created_at)).getTime();
|
81 |
+
if (latestVersionTime) {
|
82 |
+
const latestVersionInfo = `<a href="https://github.com/gaizhenbiao/chuanhuchatgpt/releases/latest" target="_blank" id="latest-version-title" style="text-decoration: none;">${latestVersion}</a>`
|
83 |
+
const manualUpdateInfo = `<a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新" target="_blanks" style="text-decoration: none;">manual update</a>`
|
84 |
+
if (localVersionTime == 0) {
|
85 |
+
const infoMessage = `Local version check failed. \nBut latest revision is ${latestVersionInfo}. \n\nWhen Update needed, \n- If you are using Docker, try to update package. \n- If you didn't use git, try ${manualUpdateInfo}.`
|
86 |
+
versionInfoElement.innerHTML = marked.parse(infoMessage, {mangle: false, headerIds: false});
|
87 |
+
console.log(`New version ${latestVersion} found!`);
|
88 |
+
disableUpdateBtn_enableCancelBtn();
|
89 |
+
} else if (localVersionTime < latestVersionTime) {
|
90 |
+
const infoMessage = `Local version check failed, it seems to be a local rivision. \n\nBut latest revision is ${latestVersionInfo}. Try ${manualUpdateInfo}.`
|
91 |
+
versionInfoElement.innerHTML = marked.parse(infoMessage, {mangle: false, headerIds: false});
|
92 |
+
console.log(`New version ${latestVersion} found!`);
|
93 |
+
disableUpdateBtn_enableCancelBtn();
|
94 |
+
// if (!isInIframe) openUpdateToast();
|
95 |
+
} else {
|
96 |
+
noUpdate("Local version check failed, it seems to be a local rivision. <br>But your revision is newer than the latest release.");
|
97 |
+
}
|
98 |
+
}
|
99 |
+
}
|
100 |
+
currentTime = new Date().getTime();
|
101 |
+
localStorage.setItem('lastCheckTime', currentTime);
|
102 |
+
} catch (error) {
|
103 |
+
console.error(error);
|
104 |
+
}
|
105 |
+
}
|
106 |
+
|
107 |
+
function getUpdateInfo() {
|
108 |
+
window.open('https://github.com/gaizhenbiao/chuanhuchatgpt/releases/latest', '_blank');
|
109 |
+
closeUpdateToast();
|
110 |
+
}
|
111 |
+
|
112 |
+
var updateSpinner = null;
|
113 |
+
|
114 |
+
function bgUpdateChuanhu() {
|
115 |
+
updateChuanhuBtn.click();
|
116 |
+
updatingInfoElement.innerText = i18n(updatingMsg_i18n);
|
117 |
+
var updatingSpinner = document.getElementById('updating-spinner');
|
118 |
+
try {
|
119 |
+
updateSpinner = new Spin.Spinner({color:'#06AE56',top:'45%',lines:9}).spin(updatingSpinner);
|
120 |
+
} catch (error) {
|
121 |
+
console.error("Can't create spinner")
|
122 |
+
}
|
123 |
+
updatingInfoElement.classList.remove('hideK');
|
124 |
+
disableUpdateBtns();
|
125 |
+
const releaseNoteWrap = document.getElementById('release-note-wrap');
|
126 |
+
releaseNoteWrap.style.setProperty('display', 'none');
|
127 |
+
statusObserver.observe(statusDisplay, { childList: true, subtree: true, characterData: true});
|
128 |
+
}
|
129 |
+
function cancelUpdate() {
|
130 |
+
closeUpdateToast();
|
131 |
+
}
|
132 |
+
function openUpdateToast() {
|
133 |
+
showingUpdateInfo = true;
|
134 |
+
setUpdateWindowHeight();
|
135 |
+
}
|
136 |
+
function closeUpdateToast() {
|
137 |
+
updateToast.style.setProperty('top', '-500px');
|
138 |
+
showingUpdateInfo = false;
|
139 |
+
if (updatingInfoElement.classList.contains('hideK') === false) {
|
140 |
+
updatingInfoElement.classList.add('hideK');
|
141 |
+
}
|
142 |
+
}
|
143 |
+
function manualCheckUpdate() {
|
144 |
+
openUpdateToast();
|
145 |
+
updateLatestVersion();
|
146 |
+
currentTime = new Date().getTime();
|
147 |
+
localStorage.setItem('lastCheckTime', currentTime);
|
148 |
+
}
|
149 |
+
function noUpdate(message="") {
|
150 |
+
localStorage.setItem('isLatestVersion', 'true');
|
151 |
+
isLatestVersion = true;
|
152 |
+
noUpdateHtml(message);
|
153 |
+
}
|
154 |
+
function noUpdateHtml(message="") {
|
155 |
+
const versionInfoElement = document.getElementById('version-info-title');
|
156 |
+
const gotoUpdateBtn = document.getElementById('goto-update-btn');
|
157 |
+
const closeUpdateBtn = document.getElementById('close-update-btn');
|
158 |
+
const releaseNoteWrap = document.getElementById('release-note-wrap');
|
159 |
+
releaseNoteWrap.style.setProperty('display', 'none');
|
160 |
+
if (message === "") {
|
161 |
+
versionInfoElement.textContent = i18n(usingLatest_i18n)
|
162 |
+
} else {
|
163 |
+
versionInfoElement.innerHTML = message;
|
164 |
+
}
|
165 |
+
gotoUpdateBtn.classList.add('hideK');
|
166 |
+
closeUpdateBtn.classList.remove('hideK');
|
167 |
+
}
|
168 |
+
|
169 |
+
var updateStatus = null;
|
170 |
+
function getUpdateStatus() {
|
171 |
+
updateStatus = statusDisplay.querySelector("#update-status");
|
172 |
+
if (updateStatus) {
|
173 |
+
return updateStatus.innerText;
|
174 |
+
} else {
|
175 |
+
return "unknown";
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
function disableUpdateBtns() {
|
180 |
+
const updatesButtons = document.querySelectorAll('.btn-update');
|
181 |
+
updatesButtons.forEach( function (btn) {
|
182 |
+
btn.disabled = true;
|
183 |
+
});
|
184 |
+
}
|
185 |
+
function enableUpdateBtns() {
|
186 |
+
const updatesButtons = document.querySelectorAll('.btn-update');
|
187 |
+
updatesButtons.forEach( function (btn) {
|
188 |
+
btn.disabled = false;
|
189 |
+
});
|
190 |
+
}
|
191 |
+
function disableUpdateBtn_enableCancelBtn() {
|
192 |
+
document.querySelector('#update-button.btn-update').disabled = true;
|
193 |
+
document.querySelector('#cancel-button.btn-update').disabled = false;
|
194 |
+
}
|
195 |
+
|
196 |
+
function setUpdateWindowHeight() {
|
197 |
+
if (!showingUpdateInfo) {return;}
|
198 |
+
const scrollPosition = window.scrollY;
|
199 |
+
// const originalTop = updateToast.style.getPropertyValue('top');
|
200 |
+
const resultTop = scrollPosition - 20 + 'px';
|
201 |
+
updateToast.style.setProperty('top', resultTop);
|
202 |
+
}
|
web_assets/javascript/user-info.js
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
var userLogged = false;
|
3 |
+
var usernameGotten = false;
|
4 |
+
var username = null;
|
5 |
+
|
6 |
+
|
7 |
+
function getUserInfo() {
|
8 |
+
if (usernameGotten) {
|
9 |
+
return;
|
10 |
+
}
|
11 |
+
userLogged = localStorage.getItem('userLogged');
|
12 |
+
if (userLogged) {
|
13 |
+
username = userInfoDiv.innerText;
|
14 |
+
if (username) {
|
15 |
+
if (username.includes("getting user info…")) {
|
16 |
+
setTimeout(getUserInfo, 500);
|
17 |
+
return;
|
18 |
+
} else if (username === " ") {
|
19 |
+
localStorage.removeItem("username");
|
20 |
+
localStorage.removeItem("userLogged")
|
21 |
+
userLogged = false;
|
22 |
+
usernameGotten = true;
|
23 |
+
return;
|
24 |
+
} else {
|
25 |
+
username = username.match(/User:\s*(.*)/)[1] || username;
|
26 |
+
localStorage.setItem("username", username);
|
27 |
+
usernameGotten = true;
|
28 |
+
clearHistoryHtml();
|
29 |
+
}
|
30 |
+
}
|
31 |
+
}
|
32 |
+
}
|
33 |
+
|
34 |
+
function showOrHideUserInfo() {
|
35 |
+
function toggleUserInfoVisibility(shouldHide) {
|
36 |
+
if (userInfoDiv) {
|
37 |
+
if (shouldHide) {
|
38 |
+
userInfoDiv.classList.add("info-transparent");
|
39 |
+
} else {
|
40 |
+
userInfoDiv.classList.remove("info-transparent");
|
41 |
+
}
|
42 |
+
}
|
43 |
+
}
|
44 |
+
|
45 |
+
// When webpage loaded, hide user info after 2 second
|
46 |
+
setTimeout(function () {
|
47 |
+
toggleUserInfoVisibility(true);
|
48 |
+
}, 2000);
|
49 |
+
|
50 |
+
let triggerElements = {appTitleDiv, userInfoDiv, sendBtn};
|
51 |
+
for (let elem in triggerElements) {
|
52 |
+
triggerElements[elem].addEventListener("mouseenter", function () {
|
53 |
+
toggleUserInfoVisibility(false);
|
54 |
+
});
|
55 |
+
triggerElements[elem].addEventListener("mouseleave", function () {
|
56 |
+
toggleUserInfoVisibility(true);
|
57 |
+
});
|
58 |
+
triggerElements[elem].ontouchstart = function () {
|
59 |
+
toggleUserInfoVisibility(false);
|
60 |
+
};
|
61 |
+
triggerElements[elem].ontouchend = function () {
|
62 |
+
setTimeout(function () {
|
63 |
+
toggleUserInfoVisibility(true);
|
64 |
+
}, 3000);
|
65 |
+
};
|
66 |
+
}
|
67 |
+
}
|
web_assets/javascript/utils.js
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
var gradioUploader = null;
|
3 |
+
|
4 |
+
function testUpload(target) {
|
5 |
+
gradioUploader = gradioApp().querySelector("#upload-index-file > .center.flex");
|
6 |
+
let uploaderEvents = ["click", "drag", "dragend", "dragenter", "dragleave", "dragover", "dragstart", "drop"];
|
7 |
+
transEventListeners(target, gradioUploader, uploaderEvents);
|
8 |
+
}
|
9 |
+
|
10 |
+
|
11 |
+
function transEventListeners(target, source, events) {
|
12 |
+
events.forEach((sourceEvent) => {
|
13 |
+
target.addEventListener(sourceEvent, function (targetEvent) {
|
14 |
+
if(targetEvent.preventDefault) targetEvent.preventDefault();
|
15 |
+
if(targetEvent.stopPropagation) targetEvent.stopPropagation();
|
16 |
+
|
17 |
+
source.dispatchEvent(new Event(sourceEvent, {detail: targetEvent.detail}));
|
18 |
+
console.log(targetEvent.detail);
|
19 |
+
});
|
20 |
+
});
|
21 |
+
}
|
22 |
+
|
23 |
+
|
24 |
+
/* NOTE: These reload functions are not used in the current version of the code.
|
25 |
+
* From stable-diffusion-webui
|
26 |
+
*/
|
27 |
+
function restart_reload() {
|
28 |
+
document.body.innerHTML = '<h1 style="font-family:ui-monospace,monospace;margin-top:20%;color:lightgray;text-align:center;">Reloading...</h1>';
|
29 |
+
|
30 |
+
var requestPing = function () {
|
31 |
+
requestGet("./internal/ping", {}, function (data) {
|
32 |
+
location.reload();
|
33 |
+
}, function () {
|
34 |
+
setTimeout(requestPing, 500);
|
35 |
+
});
|
36 |
+
};
|
37 |
+
|
38 |
+
setTimeout(requestPing, 2000);
|
39 |
+
|
40 |
+
return [];
|
41 |
+
}
|
42 |
+
|
43 |
+
function requestGet(url, data, handler, errorHandler) {
|
44 |
+
var xhr = new XMLHttpRequest();
|
45 |
+
var args = Object.keys(data).map(function (k) {
|
46 |
+
return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]);
|
47 |
+
}).join('&');
|
48 |
+
xhr.open("GET", url + "?" + args, true);
|
49 |
+
|
50 |
+
xhr.onreadystatechange = function () {
|
51 |
+
if (xhr.readyState === 4) {
|
52 |
+
if (xhr.status === 200) {
|
53 |
+
try {
|
54 |
+
var js = JSON.parse(xhr.responseText);
|
55 |
+
handler(js);
|
56 |
+
} catch (error) {
|
57 |
+
console.error(error);
|
58 |
+
errorHandler();
|
59 |
+
}
|
60 |
+
} else {
|
61 |
+
errorHandler();
|
62 |
+
}
|
63 |
+
}
|
64 |
+
};
|
65 |
+
var js = JSON.stringify(data);
|
66 |
+
xhr.send(js);
|
67 |
+
}
|
web_assets/stylesheet/ChuanhuChat.css
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
:root {
|
2 |
+
--chatbot-color-light: #000000;
|
3 |
+
--chatbot-color-dark: #FFFFFF;
|
4 |
+
--chatbot-background-color-light: #F3F3F3;
|
5 |
+
--chatbot-background-color-dark: #121111;
|
6 |
+
--message-user-background-color-light: #95EC69;
|
7 |
+
--message-user-background-color-dark: #26B561;
|
8 |
+
--message-bot-background-color-light: #FFFFFF;
|
9 |
+
--message-bot-background-color-dark: #2C2C2C;
|
10 |
+
--switch-checkbox-color-light: #e5e7eb;
|
11 |
+
--switch-checkbox-color-dark: #515151;
|
12 |
+
}
|
13 |
+
|
14 |
+
.hideK {
|
15 |
+
display: none;
|
16 |
+
}
|
17 |
+
|
18 |
+
#app-title {
|
19 |
+
font-weight: var(--prose-header-text-weight);
|
20 |
+
font-size: var(--text-xxl);
|
21 |
+
line-height: 1.3;
|
22 |
+
text-align: left;
|
23 |
+
margin-top: 6px;
|
24 |
+
white-space: nowrap;
|
25 |
+
}
|
26 |
+
#description {
|
27 |
+
text-align: center;
|
28 |
+
margin: 32px 0 4px 0;
|
29 |
+
}
|
30 |
+
|
31 |
+
/* 高级页面 */
|
32 |
+
#advanced-warning {
|
33 |
+
display: flex;
|
34 |
+
flex-wrap: wrap;
|
35 |
+
flex-direction: column;
|
36 |
+
align-content: center;
|
37 |
+
}
|
38 |
+
|
39 |
+
#netsetting-warning hr {
|
40 |
+
margin-bottom: 1em;
|
41 |
+
}
|
42 |
+
|
43 |
+
.view-only-textbox textarea {
|
44 |
+
-webkit-text-fill-color: darkgray !important;
|
45 |
+
cursor: not-allowed !important;
|
46 |
+
}
|
47 |
+
|
48 |
+
#footer {
|
49 |
+
text-align: center;
|
50 |
+
}
|
51 |
+
#footer div {
|
52 |
+
display: inline-block;
|
53 |
+
}
|
54 |
+
#footer .versions{
|
55 |
+
font-size: 85%;
|
56 |
+
opacity: 0.60;
|
57 |
+
}
|
58 |
+
|
59 |
+
|
60 |
+
#float-display {
|
61 |
+
position: absolute;
|
62 |
+
max-height: 30px;
|
63 |
+
}
|
64 |
+
|
65 |
+
.insert-block {
|
66 |
+
position: relative;
|
67 |
+
margin: 0;
|
68 |
+
padding: 8px 12px;
|
69 |
+
box-shadow: var(--block-shadow);
|
70 |
+
border-width: var(--block-border-width);
|
71 |
+
border-color: var(--block-border-color);
|
72 |
+
border-radius: var(--block-radius);
|
73 |
+
background: var(--block-background-fill);
|
74 |
+
width: 100%;
|
75 |
+
line-height: var(--line-sm);
|
76 |
+
min-height: 2em;
|
77 |
+
}
|
78 |
+
|
79 |
+
/* status-display */
|
80 |
+
#status-display {
|
81 |
+
display: flex;
|
82 |
+
min-height: 2em;
|
83 |
+
align-items: flex-end;
|
84 |
+
justify-content: flex-end;
|
85 |
+
transition: all 0.6s;
|
86 |
+
}
|
87 |
+
#status-display p {
|
88 |
+
font-size: .85em;
|
89 |
+
font-family: ui-monospace, "SF Mono", "SFMono-Regular", "Menlo", "Consolas", "Liberation Mono", "Microsoft Yahei UI", "Microsoft Yahei", monospace;
|
90 |
+
/* Windows下中文的monospace会fallback为新宋体,实在太丑,这里折中使用微软雅黑 */
|
91 |
+
color: var(--body-text-color-subdued);
|
92 |
+
}
|
93 |
+
|
94 |
+
|
95 |
+
#submit-btn, #cancel-btn {
|
96 |
+
height: 40px !important;
|
97 |
+
}
|
98 |
+
#submit-btn::before {
|
99 |
+
content: url("data:image/svg+xml, %3Csvg width='21px' height='20px' viewBox='0 0 21 20' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E %3Cg id='page' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E %3Cg id='send' transform='translate(0.435849, 0.088463)' fill='%23FFFFFF' fill-rule='nonzero'%3E %3Cpath d='M0.579148261,0.0428666046 C0.301105539,-0.0961547561 -0.036517765,0.122307382 0.0032026237,0.420210298 L1.4927172,18.1553639 C1.5125774,18.4334066 1.79062012,18.5922882 2.04880264,18.4929872 L8.24518329,15.8913017 L11.6412765,19.7441794 C11.8597387,19.9825018 12.2370824,19.8832008 12.3165231,19.5852979 L13.9450591,13.4882182 L19.7839562,11.0255541 C20.0619989,10.8865327 20.0818591,10.4694687 19.7839562,10.3105871 L0.579148261,0.0428666046 Z M11.6138902,17.0883151 L9.85385903,14.7195502 L0.718169621,0.618812241 L12.69945,12.9346347 L11.6138902,17.0883151 Z' id='shape'%3E%3C/path%3E %3C/g%3E %3C/g%3E %3C/svg%3E");
|
100 |
+
height: 21px;
|
101 |
+
}
|
102 |
+
#cancel-btn::before {
|
103 |
+
content: url("data:image/svg+xml,%3Csvg width='21px' height='21px' viewBox='0 0 21 21' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E %3Cg id='pg' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E %3Cpath d='M10.2072007,20.088463 C11.5727865,20.088463 12.8594566,19.8259823 14.067211,19.3010209 C15.2749653,18.7760595 16.3386126,18.0538087 17.2581528,17.1342685 C18.177693,16.2147282 18.8982283,15.1527965 19.4197586,13.9484733 C19.9412889,12.7441501 20.202054,11.4557644 20.202054,10.0833163 C20.202054,8.71773046 19.9395733,7.43106036 19.4146119,6.22330603 C18.8896505,5.01555169 18.1673997,3.95018885 17.2478595,3.0272175 C16.3283192,2.10424615 15.2646719,1.3837109 14.0569176,0.865611739 C12.8491633,0.34751258 11.5624932,0.088463 10.1969073,0.088463 C8.83132146,0.088463 7.54636692,0.34751258 6.34204371,0.865611739 C5.1377205,1.3837109 4.07407321,2.10424615 3.15110186,3.0272175 C2.22813051,3.95018885 1.5058797,5.01555169 0.984349419,6.22330603 C0.46281914,7.43106036 0.202054,8.71773046 0.202054,10.0833163 C0.202054,11.4557644 0.4645347,12.7441501 0.9894961,13.9484733 C1.5144575,15.1527965 2.23670831,16.2147282 3.15624854,17.1342685 C4.07578877,18.0538087 5.1377205,18.7760595 6.34204371,19.3010209 C7.54636692,19.8259823 8.83475258,20.088463 10.2072007,20.088463 Z M10.2072007,18.2562448 C9.07493099,18.2562448 8.01471483,18.0452309 7.0265522,17.6232031 C6.03838956,17.2011753 5.17031614,16.6161693 4.42233192,15.8681851 C3.6743477,15.1202009 3.09105726,14.2521274 2.67246059,13.2639648 C2.25386392,12.2758022 2.04456558,11.215586 2.04456558,10.0833163 C2.04456558,8.95104663 2.25386392,7.89083047 2.67246059,6.90266784 C3.09105726,5.9145052 3.6743477,5.04643178 4.42233192,4.29844756 C5.17031614,3.55046334 6.036674,2.9671729 7.02140552,2.54857623 C8.00613703,2.12997956 9.06463763,1.92068122 10.1969073,1.92068122 C11.329177,1.92068122 12.3911087,2.12997956 13.3827025,2.54857623 C14.3742962,2.9671729 15.2440852,3.55046334 15.9920694,4.29844756 C16.7400537,5.04643178 17.3233441,5.9145052 17.7419408,6.90266784 C18.1605374,7.89083047 18.3698358,8.95104663 18.3698358,10.0833163 C18.3698358,11.215586 18.1605374,12.2758022 17.7419408,13.2639648 C17.3233441,14.2521274 16.7400537,15.1202009 15.9920694,15.8681851 C15.2440852,16.6161693 14.3760118,17.2011753 13.3878492,17.6232031 C12.3996865,18.0452309 11.3394704,18.2562448 10.2072007,18.2562448 Z M7.65444721,13.6242324 L12.7496608,13.6242324 C13.0584616,13.6242324 13.3003556,13.5384544 13.4753427,13.3668984 C13.6503299,13.1953424 13.7378234,12.9585951 13.7378234,12.6566565 L13.7378234,7.49968276 C13.7378234,7.19774418 13.6503299,6.96099688 13.4753427,6.78944087 C13.3003556,6.61788486 13.0584616,6.53210685 12.7496608,6.53210685 L7.65444721,6.53210685 C7.33878414,6.53210685 7.09345904,6.61788486 6.91847191,6.78944087 C6.74348478,6.96099688 6.65599121,7.19774418 6.65599121,7.49968276 L6.65599121,12.6566565 C6.65599121,12.9585951 6.74348478,13.1953424 6.91847191,13.3668984 C7.09345904,13.5384544 7.33878414,13.6242324 7.65444721,13.6242324 Z' id='shape' fill='%23FF3B30' fill-rule='nonzero'%3E%3C/path%3E %3C/g%3E %3C/svg%3E");
|
104 |
+
height: 21px;
|
105 |
+
}
|
web_assets/stylesheet/chatbot.css
ADDED
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
hr.append-display {
|
3 |
+
margin: 8px 0;
|
4 |
+
border: none;
|
5 |
+
height: 1px;
|
6 |
+
border-top-width: 0;
|
7 |
+
background-image: linear-gradient(to right, rgba(50,50,50, 0.1), rgba(150, 150, 150, 0.8), rgba(50,50,50, 0.1));
|
8 |
+
}
|
9 |
+
.source-a {
|
10 |
+
font-size: 0.8em;
|
11 |
+
max-width: 100%;
|
12 |
+
margin: 0;
|
13 |
+
display: flex;
|
14 |
+
flex-direction: row;
|
15 |
+
flex-wrap: wrap;
|
16 |
+
align-items: center;
|
17 |
+
/* background-color: #dddddd88; */
|
18 |
+
border-radius: 1.5rem;
|
19 |
+
padding: 0.2em;
|
20 |
+
}
|
21 |
+
.source-a a {
|
22 |
+
display: inline-block;
|
23 |
+
background-color: #aaaaaa50;
|
24 |
+
border-radius: 1rem;
|
25 |
+
padding: 0.5em;
|
26 |
+
text-align: center;
|
27 |
+
text-overflow: ellipsis;
|
28 |
+
overflow: hidden;
|
29 |
+
min-width: 20%;
|
30 |
+
white-space: nowrap;
|
31 |
+
margin: 0.2rem 0.1rem;
|
32 |
+
text-decoration: none !important;
|
33 |
+
flex: 1;
|
34 |
+
transition: flex 0.5s;
|
35 |
+
}
|
36 |
+
.source-a a:hover {
|
37 |
+
background-color: #aaaaaa20;
|
38 |
+
flex: 2;
|
39 |
+
}
|
40 |
+
|
41 |
+
/* 川虎助理 */
|
42 |
+
.agent-prefix {
|
43 |
+
font-size: smaller;
|
44 |
+
opacity: 0.6;
|
45 |
+
padding: 6px 0 4px;
|
46 |
+
}
|
47 |
+
.agent-prefix::before {
|
48 |
+
content: '🐯';
|
49 |
+
filter: grayscale();
|
50 |
+
padding: 0 4px;
|
51 |
+
}
|
52 |
+
|
53 |
+
/* 亮色(默认) */
|
54 |
+
#chuanhu-chatbot {
|
55 |
+
background-color: var(--chatbot-background-color-light) !important;
|
56 |
+
color: var(--chatbot-color-light) !important;
|
57 |
+
}
|
58 |
+
[data-testid = "bot"] {
|
59 |
+
background-color: var(--message-bot-background-color-light) !important;
|
60 |
+
}
|
61 |
+
[data-testid = "user"] {
|
62 |
+
background-color: var(--message-user-background-color-light) !important;
|
63 |
+
}
|
64 |
+
/* 暗色 */
|
65 |
+
.dark #chuanhu-chatbot {
|
66 |
+
background-color: var(--chatbot-background-color-dark) !important;
|
67 |
+
color: var(--chatbot-color-dark) !important;
|
68 |
+
}
|
69 |
+
.dark [data-testid = "bot"] {
|
70 |
+
background-color: var(--message-bot-background-color-dark) !important;
|
71 |
+
}
|
72 |
+
.dark [data-testid = "user"] {
|
73 |
+
background-color: var(--message-user-background-color-dark) !important;
|
74 |
+
}
|
75 |
+
|
76 |
+
/* 对话气泡 */
|
77 |
+
.message {
|
78 |
+
border-radius: var(--radius-xl) !important;
|
79 |
+
border: none;
|
80 |
+
padding: var(--spacing-xl) !important;
|
81 |
+
font-size: var(--text-md) !important;
|
82 |
+
line-height: var(--line-md) !important;
|
83 |
+
min-height: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
|
84 |
+
min-width: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
|
85 |
+
}
|
86 |
+
[data-testid = "bot"] {
|
87 |
+
max-width: 85%;
|
88 |
+
border-bottom-left-radius: 0 !important;
|
89 |
+
}
|
90 |
+
[data-testid = "user"] {
|
91 |
+
max-width: 85%;
|
92 |
+
width: auto !important;
|
93 |
+
border-bottom-right-radius: 0 !important;
|
94 |
+
}
|
95 |
+
|
96 |
+
/* 屏幕宽度大于等于500px的设备 */
|
97 |
+
/* update on 2023.4.8: 高度的细致调整已写入JavaScript */
|
98 |
+
@media screen and (min-width: 500px) {
|
99 |
+
#chuanhu-chatbot {
|
100 |
+
height: calc(100vh - 200px);
|
101 |
+
}
|
102 |
+
#chuanhu-chatbot>.wrapper>.wrap {
|
103 |
+
max-height: calc(100vh - 200px - var(--line-sm)*1rem - 2*var(--block-label-margin) );
|
104 |
+
}
|
105 |
+
}
|
106 |
+
/* 屏幕宽度小于500px的设备 */
|
107 |
+
@media screen and (max-width: 499px) {
|
108 |
+
#chuanhu-chatbot {
|
109 |
+
height: calc(100vh - 140px);
|
110 |
+
}
|
111 |
+
#chuanhu-chatbot>.wrapper>.wrap {
|
112 |
+
max-height: calc(100vh - 140px - var(--line-sm)*1rem - 2*var(--block-label-margin) );
|
113 |
+
}
|
114 |
+
[data-testid = "bot"] {
|
115 |
+
max-width: 95% !important;
|
116 |
+
}
|
117 |
+
#app-title h1{
|
118 |
+
letter-spacing: -1px; font-size: 22px;
|
119 |
+
}
|
120 |
+
}
|
121 |
+
|
122 |
+
#chuanhu-chatbot>.wrapper>.wrap {
|
123 |
+
overflow-x: hidden;
|
124 |
+
}
|
125 |
+
|
126 |
+
.message.user p {
|
127 |
+
white-space: pre-wrap;
|
128 |
+
}
|
129 |
+
.message .user-message {
|
130 |
+
display: block;
|
131 |
+
padding: 0 !important;
|
132 |
+
white-space: pre-wrap;
|
133 |
+
}
|
134 |
+
|
135 |
+
.message .md-message p {
|
136 |
+
margin-top: 0.6em !important;
|
137 |
+
margin-bottom: 0.6em !important;
|
138 |
+
}
|
139 |
+
.message .md-message p:first-child { margin-top: 0 !important; }
|
140 |
+
.message .md-message p:last-of-type { margin-bottom: 0 !important; }
|
141 |
+
|
142 |
+
.message .md-message {
|
143 |
+
display: block;
|
144 |
+
padding: 0 !important;
|
145 |
+
}
|
146 |
+
.message .raw-message p {
|
147 |
+
margin:0 !important;
|
148 |
+
}
|
149 |
+
.message .raw-message {
|
150 |
+
display: block;
|
151 |
+
padding: 0 !important;
|
152 |
+
white-space: pre-wrap;
|
153 |
+
}
|
154 |
+
.message .hideM {
|
155 |
+
display: none;
|
156 |
+
}
|
157 |
+
|
158 |
+
/* custom buttons */
|
159 |
+
.chuanhu-btn {
|
160 |
+
border-radius: 5px;
|
161 |
+
/* background-color: #E6E6E6 !important; */
|
162 |
+
color: rgba(120, 120, 120, 0.64) !important;
|
163 |
+
padding: 4px !important;
|
164 |
+
position: absolute;
|
165 |
+
right: -22px;
|
166 |
+
cursor: pointer !important;
|
167 |
+
transition: color .2s ease, background-color .2s ease;
|
168 |
+
}
|
169 |
+
.chuanhu-btn:hover {
|
170 |
+
background-color: rgba(167, 167, 167, 0.25) !important;
|
171 |
+
color: unset !important;
|
172 |
+
}
|
173 |
+
.chuanhu-btn:active {
|
174 |
+
background-color: rgba(167, 167, 167, 0.5) !important;
|
175 |
+
}
|
176 |
+
.chuanhu-btn:focus {
|
177 |
+
outline: none;
|
178 |
+
}
|
179 |
+
|
180 |
+
.copy-bot-btn {
|
181 |
+
/* top: 18px; */
|
182 |
+
bottom: 0;
|
183 |
+
}
|
184 |
+
.toggle-md-btn {
|
185 |
+
/* top: 0; */
|
186 |
+
bottom: 20px;
|
187 |
+
}
|
188 |
+
|
189 |
+
/* note: this is deprecated */
|
190 |
+
.copy-code-btn {
|
191 |
+
position: relative;
|
192 |
+
float: right;
|
193 |
+
font-size: 1em;
|
194 |
+
cursor: pointer;
|
195 |
+
}
|
196 |
+
/* note: the button below disabled in chatbot.py */
|
197 |
+
.message div.icon-button > button[title="copy"] {
|
198 |
+
display: none;
|
199 |
+
}
|
200 |
+
|
201 |
+
|
202 |
+
/* history message */
|
203 |
+
.wrapper>.wrap>.history-message {
|
204 |
+
padding: 10px !important;
|
205 |
+
}
|
206 |
+
.history-message {
|
207 |
+
/* padding: 0 !important; */
|
208 |
+
opacity: 80%;
|
209 |
+
display: flex;
|
210 |
+
flex-direction: column;
|
211 |
+
}
|
212 |
+
.history-message>.history-message {
|
213 |
+
padding: 0 !important;
|
214 |
+
}
|
215 |
+
.history-message>.message-wrap {
|
216 |
+
padding: 0 !important;
|
217 |
+
margin-bottom: 16px;
|
218 |
+
}
|
219 |
+
.history-message>.message {
|
220 |
+
margin-bottom: 16px;
|
221 |
+
}
|
222 |
+
.wrapper>.wrap>.history-message::after {
|
223 |
+
content: "";
|
224 |
+
display: block;
|
225 |
+
height: 2px;
|
226 |
+
background-color: var(--body-text-color-subdued);
|
227 |
+
margin-bottom: 10px;
|
228 |
+
margin-top: -10px;
|
229 |
+
clear: both;
|
230 |
+
}
|
231 |
+
.wrapper>.wrap>.history-message>:last-child::after {
|
232 |
+
content: "仅供查看";
|
233 |
+
display: block;
|
234 |
+
text-align: center;
|
235 |
+
color: var(--body-text-color-subdued);
|
236 |
+
font-size: 0.8em;
|
237 |
+
}
|
238 |
+
|
239 |
+
/* #chuanhu-chatbot {
|
240 |
+
transition: height 0.3s ease;
|
241 |
+
note: find it better without transition animation...;
|
242 |
+
} */
|
web_assets/stylesheet/custom-components.css
ADDED
@@ -0,0 +1,240 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
/* user-info */
|
3 |
+
#user-info.block {
|
4 |
+
white-space: nowrap;
|
5 |
+
position: absolute; left: 8em; top: .8em;
|
6 |
+
z-index: var(--layer-2);
|
7 |
+
box-shadow: var(--block-shadow);
|
8 |
+
border: none!important; border-radius: var(--block-label-radius);
|
9 |
+
background: var(--color-accent);
|
10 |
+
padding: var(--block-label-padding);
|
11 |
+
font-size: var(--block-label-text-size); line-height: var(--line-sm);
|
12 |
+
width: auto; max-height: 30px!important;
|
13 |
+
opacity: 1;
|
14 |
+
transition: opacity 0.3s ease-in-out;
|
15 |
+
}
|
16 |
+
#user-info.block .wrap {
|
17 |
+
opacity: 0;
|
18 |
+
}
|
19 |
+
#user-info p {
|
20 |
+
color: white;
|
21 |
+
font-weight: var(--block-label-text-weight);
|
22 |
+
}
|
23 |
+
#user-info.info-transparent {
|
24 |
+
opacity: 0;
|
25 |
+
transition: opacity 1s ease-in-out;
|
26 |
+
}
|
27 |
+
|
28 |
+
|
29 |
+
/* updater */
|
30 |
+
#toast-update {
|
31 |
+
position: absolute;
|
32 |
+
display: flex;
|
33 |
+
top: -500px;
|
34 |
+
width: 100%;
|
35 |
+
justify-content: center;
|
36 |
+
z-index: var(--layer-top);
|
37 |
+
transition: top 0.3s ease-out;
|
38 |
+
}
|
39 |
+
#check-chuanhu-update {
|
40 |
+
position: absolute;
|
41 |
+
align-items: center;
|
42 |
+
display: flex;
|
43 |
+
flex-direction: column;
|
44 |
+
justify-content: center;
|
45 |
+
margin: var(--size-6) var(--size-4);
|
46 |
+
box-shadow: var(--shadow-drop-lg);
|
47 |
+
border: 1px solid var(--block-label-border-color);
|
48 |
+
border-radius: var(--container-radius);
|
49 |
+
background: var(--background-fill-primary);
|
50 |
+
padding: var(--size-4) var(--size-6);
|
51 |
+
min-width: 360px;
|
52 |
+
max-width: 480px;
|
53 |
+
overflow: hidden;
|
54 |
+
pointer-events: auto;
|
55 |
+
}
|
56 |
+
#version-info-title {
|
57 |
+
font-size: 1.2em;
|
58 |
+
font-weight: bold;
|
59 |
+
text-align: start;
|
60 |
+
width: 100%;
|
61 |
+
}
|
62 |
+
#release-note-wrap {
|
63 |
+
width: 100%;
|
64 |
+
max-width: 400px;
|
65 |
+
height: 120px;
|
66 |
+
border: solid 1px var(--border-color-primary);
|
67 |
+
overflow: auto;
|
68 |
+
padding: 0 8px;
|
69 |
+
}
|
70 |
+
#release-note-wrap.hideK {
|
71 |
+
display: none;
|
72 |
+
}
|
73 |
+
.btn-update-group {
|
74 |
+
display: flex;
|
75 |
+
justify-content: space-evenly;
|
76 |
+
align-items: center;
|
77 |
+
width: 100%;
|
78 |
+
padding-top: 10px;
|
79 |
+
}
|
80 |
+
.btn-update-group.hideK {
|
81 |
+
display: none;
|
82 |
+
}
|
83 |
+
#updating-info {
|
84 |
+
margin: 16px 0px 24px;
|
85 |
+
text-align: start;
|
86 |
+
width: 100%;
|
87 |
+
}
|
88 |
+
|
89 |
+
|
90 |
+
#usage-display p, #usage-display span {
|
91 |
+
margin: 0;
|
92 |
+
font-size: .85em;
|
93 |
+
color: var(--body-text-color-subdued);
|
94 |
+
}
|
95 |
+
.progress-bar {
|
96 |
+
background-color: var(--input-background-fill);;
|
97 |
+
margin: .5em 0 !important;
|
98 |
+
height: 20px;
|
99 |
+
border-radius: 10px;
|
100 |
+
overflow: hidden;
|
101 |
+
}
|
102 |
+
.progress {
|
103 |
+
background-color: var(--block-title-background-fill);
|
104 |
+
height: 100%;
|
105 |
+
border-radius: 10px;
|
106 |
+
text-align: right;
|
107 |
+
transition: width 0.5s ease-in-out;
|
108 |
+
}
|
109 |
+
.progress-text {
|
110 |
+
/* color: white; */
|
111 |
+
color: var(--color-accent) !important;
|
112 |
+
font-size: 1em !important;
|
113 |
+
font-weight: bold;
|
114 |
+
padding-right: 10px;
|
115 |
+
line-height: 20px;
|
116 |
+
}
|
117 |
+
|
118 |
+
|
119 |
+
/* 亮暗色模式切换 */
|
120 |
+
#apSwitch input[type="checkbox"] {
|
121 |
+
margin: 0 !important;
|
122 |
+
}
|
123 |
+
#apSwitch label.apSwitch {
|
124 |
+
display: flex;
|
125 |
+
align-items: center;
|
126 |
+
cursor: pointer;
|
127 |
+
color: var(--body-text-color);
|
128 |
+
font-weight: var(--checkbox-label-text-weight);
|
129 |
+
font-size: var(--checkbox-label-text-size);
|
130 |
+
line-height: var(--line-md);
|
131 |
+
margin: 2px 0 !important;
|
132 |
+
}
|
133 |
+
input[type="checkbox"]#apSwitch-checkbox::before {
|
134 |
+
background: none !important;
|
135 |
+
content: '🌞';
|
136 |
+
border: none !important;
|
137 |
+
box-shadow: none !important;
|
138 |
+
font-size: 22px;
|
139 |
+
top: -4.4px;
|
140 |
+
left: -1px;
|
141 |
+
}
|
142 |
+
input:checked[type="checkbox"]#apSwitch-checkbox::before {
|
143 |
+
content: '🌚';
|
144 |
+
left: 16px;
|
145 |
+
}
|
146 |
+
|
147 |
+
/* .apSwitch {
|
148 |
+
top: 2px;
|
149 |
+
display: inline-block;
|
150 |
+
height: 22px;
|
151 |
+
position: relative;
|
152 |
+
width: 40px;
|
153 |
+
border-radius: 11px;
|
154 |
+
box-shadow: inset 0 0 1px 0 rgba(0,0,0,0.05), inset 0 0 2px 0 rgba(0,0,0,0.08) !important;
|
155 |
+
}
|
156 |
+
.apSwitch input {
|
157 |
+
display: none !important;
|
158 |
+
}
|
159 |
+
.apSlider {
|
160 |
+
background-color: var(--neutral-200);
|
161 |
+
bottom: 0;
|
162 |
+
cursor: pointer;
|
163 |
+
left: 0;
|
164 |
+
position: absolute;
|
165 |
+
right: 0;
|
166 |
+
top: 0;
|
167 |
+
transition: .4s;
|
168 |
+
font-size: 22px;
|
169 |
+
border-radius: 11px;
|
170 |
+
}
|
171 |
+
.apSlider::before {
|
172 |
+
transform: scale(0.9);
|
173 |
+
position: absolute;
|
174 |
+
transition: .4s;
|
175 |
+
content: "🌞";
|
176 |
+
}
|
177 |
+
input:checked + .apSlider {
|
178 |
+
background-color: var(--primary-600);
|
179 |
+
}
|
180 |
+
input:checked + .apSlider::before {
|
181 |
+
transform: translateX(18px);
|
182 |
+
content:"🌚";
|
183 |
+
} */
|
184 |
+
|
185 |
+
/* switch-checkbox */
|
186 |
+
.switch-checkbox label {
|
187 |
+
flex-direction: row-reverse;
|
188 |
+
justify-content: space-between;
|
189 |
+
}
|
190 |
+
.switch-checkbox input[type="checkbox"] + span {
|
191 |
+
margin-left: 0 !important;
|
192 |
+
}
|
193 |
+
|
194 |
+
.switch-checkbox input[type="checkbox"] {
|
195 |
+
-moz-appearance: none;
|
196 |
+
appearance: none;
|
197 |
+
-webkit-appearance: none;
|
198 |
+
outline: none;
|
199 |
+
}
|
200 |
+
|
201 |
+
.switch-checkbox input[type="checkbox"] {
|
202 |
+
display: inline-block !important;
|
203 |
+
position: relative !important;
|
204 |
+
border: none !important;
|
205 |
+
outline: none;
|
206 |
+
width: 40px !important;
|
207 |
+
height: 22px !important;
|
208 |
+
border-radius: 11px !important;
|
209 |
+
background-image: none !important;
|
210 |
+
box-shadow: inset 0 0 1px 0 rgba(0,0,0,0.05), inset 0 0 2px 0 rgba(0,0,0,0.08) !important;
|
211 |
+
background-image: none !important;
|
212 |
+
background-color: var(--switch-checkbox-color-light) !important;
|
213 |
+
transition: .2s ease background-color;
|
214 |
+
}
|
215 |
+
.dark .switch-checkbox input[type="checkbox"] {
|
216 |
+
background-color: var(--switch-checkbox-color-dark) !important;
|
217 |
+
}
|
218 |
+
.switch-checkbox input[type="checkbox"]::before {
|
219 |
+
content: "";
|
220 |
+
position: absolute;
|
221 |
+
width: 22px;
|
222 |
+
height: 22px;
|
223 |
+
top: 0;
|
224 |
+
left: 0;
|
225 |
+
background: #FFFFFF;
|
226 |
+
border: 0.5px solid rgba(0,0,0,0.02);
|
227 |
+
box-shadow: 0 0 0 0 rgba(0,0,0,0.15), 0 1px 0 0 rgba(0,0,0,0.05);
|
228 |
+
transform: scale(0.9);
|
229 |
+
border-radius: 11px !important;
|
230 |
+
transition: .4s ease all;
|
231 |
+
box-shadow: var(--input-shadow);
|
232 |
+
}
|
233 |
+
.switch-checkbox input:checked[type="checkbox"] {
|
234 |
+
background-color: var(--primary-600) !important;
|
235 |
+
}
|
236 |
+
.switch-checkbox input:checked[type="checkbox"]::before {
|
237 |
+
background-color: #fff;
|
238 |
+
left: 18px;
|
239 |
+
}
|
240 |
+
|
web_assets/stylesheet/markdown.css
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
.message-wrap>div img{
|
3 |
+
border-radius: 10px !important;
|
4 |
+
}
|
5 |
+
|
6 |
+
/* 表格 */
|
7 |
+
.message table {
|
8 |
+
margin: 1em 0;
|
9 |
+
border-collapse: collapse;
|
10 |
+
empty-cells: show;
|
11 |
+
}
|
12 |
+
.message td, .message th {
|
13 |
+
border: 1.2px solid var(--border-color-primary) !important;
|
14 |
+
padding: 0.2em;
|
15 |
+
}
|
16 |
+
.message thead {
|
17 |
+
background-color: rgba(175,184,193,0.2);
|
18 |
+
}
|
19 |
+
.message thead th {
|
20 |
+
padding: .5em .2em;
|
21 |
+
}
|
22 |
+
|
23 |
+
/* 行内代码 */
|
24 |
+
.message :not(pre) code {
|
25 |
+
display: inline;
|
26 |
+
white-space: break-spaces;
|
27 |
+
font-family: var(--font-mono);
|
28 |
+
border-radius: 6px;
|
29 |
+
margin: 0 2px 0 2px;
|
30 |
+
padding: .2em .4em .1em .4em;
|
31 |
+
background-color: rgba(175,184,193,0.2);
|
32 |
+
}
|
33 |
+
/* 代码块 */
|
34 |
+
.message pre,
|
35 |
+
.message pre[class*=language-] {
|
36 |
+
color: #fff;
|
37 |
+
overflow-x: auto;
|
38 |
+
overflow-y: hidden;
|
39 |
+
margin: .8em 1em 1em 0em !important;
|
40 |
+
padding: var(--spacing-xl) 1.2em !important;
|
41 |
+
border-radius: var(--radius-lg) !important;
|
42 |
+
}
|
43 |
+
.message pre code,
|
44 |
+
.message pre code[class*=language-] {
|
45 |
+
color: #fff;
|
46 |
+
padding: 0;
|
47 |
+
margin: 0;
|
48 |
+
background-color: unset;
|
49 |
+
text-shadow: none;
|
50 |
+
font-family: var(--font-mono);
|
51 |
+
}
|
52 |
+
|
53 |
+
|
54 |
+
/* 覆盖prism.css */
|
55 |
+
.language-css .token.string,
|
56 |
+
.style .token.string,
|
57 |
+
.token.entity,
|
58 |
+
.token.operator,
|
59 |
+
.token.url {
|
60 |
+
background: none !important;
|
61 |
+
}
|
web_assets/stylesheet/override-gradio.css
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
/* 解决container=False时的错误填充 */
|
3 |
+
div.form {
|
4 |
+
background: none !important;
|
5 |
+
}
|
6 |
+
div.no-container {
|
7 |
+
padding: 10px 0 0 0 !important;
|
8 |
+
}
|
9 |
+
|
10 |
+
/* gradio的页脚信息 */
|
11 |
+
footer {
|
12 |
+
/* display: none !important; */
|
13 |
+
margin-top: .2em !important;
|
14 |
+
font-size: 85%;
|
15 |
+
}
|
16 |
+
|
17 |
+
/* 覆盖 gradio 丑陋的复制按钮样式 */
|
18 |
+
.message pre button[title="copy"] {
|
19 |
+
border-radius: 5px;
|
20 |
+
transition: background-color .2s ease;
|
21 |
+
}
|
22 |
+
.message pre button[title="copy"]:hover {
|
23 |
+
background-color: #333232;
|
24 |
+
}
|
25 |
+
.message pre button .check {
|
26 |
+
color: #fff !important;
|
27 |
+
background: var(--neutral-950) !important;
|
28 |
+
}
|
29 |
+
|
30 |
+
|
31 |
+
|
32 |
+
|
33 |
+
/* Override Slider Styles (for webkit browsers like Safari and Chrome)
|
34 |
+
* 好希望这份提案能早日实现 https://github.com/w3c/csswg-drafts/issues/4410
|
35 |
+
* 进度滑块在各个平台还是太不统一了
|
36 |
+
**/
|
37 |
+
|
38 |
+
input[type="range"] {
|
39 |
+
/* -webkit-appearance: none; */
|
40 |
+
appearance: none;
|
41 |
+
height: 4px;
|
42 |
+
background: var(--input-background-fill);
|
43 |
+
border-radius: 5px;
|
44 |
+
background-image: linear-gradient(var(--primary-500),var(--primary-500));
|
45 |
+
background-size: 0% 100%;
|
46 |
+
background-repeat: no-repeat;
|
47 |
+
}
|
48 |
+
input[type="range"]::-webkit-slider-thumb {
|
49 |
+
-webkit-appearance: none;
|
50 |
+
height: 20px;
|
51 |
+
width: 20px;
|
52 |
+
border-radius: 50%;
|
53 |
+
border: solid 0.5px #ddd;
|
54 |
+
background-color: white;
|
55 |
+
cursor: ew-resize;
|
56 |
+
box-shadow: var(--input-shadow);
|
57 |
+
transition: background-color .1s ease;
|
58 |
+
}
|
59 |
+
input[type="range"]::-webkit-slider-thumb:hover {
|
60 |
+
background: var(--neutral-50);
|
61 |
+
}
|
62 |
+
input[type=range]::-webkit-slider-runnable-track {
|
63 |
+
-webkit-appearance: none;
|
64 |
+
box-shadow: none;
|
65 |
+
border: none;
|
66 |
+
background: transparent;
|
67 |
+
}
|