JohnSmith9982 commited on
Commit
fcf8580
1 Parent(s): 8993361

Upload 63 files

Browse files
Files changed (42) hide show
  1. ChuanhuChatbot.py +109 -58
  2. config_example.json +48 -14
  3. modules/config.py +106 -27
  4. modules/index_func.py +13 -5
  5. modules/models/Google_PaLM.py +26 -0
  6. modules/models/__pycache__/ChuanhuAgent.cpython-311.pyc +0 -0
  7. modules/models/__pycache__/Google_PaLM.cpython-311.pyc +0 -0
  8. modules/models/__pycache__/azure.cpython-311.pyc +0 -0
  9. modules/models/__pycache__/base_model.cpython-311.pyc +0 -0
  10. modules/models/__pycache__/base_model.cpython-39.pyc +0 -0
  11. modules/models/__pycache__/models.cpython-311.pyc +0 -0
  12. modules/models/__pycache__/models.cpython-39.pyc +0 -0
  13. modules/models/azure.py +17 -0
  14. modules/models/base_model.py +127 -28
  15. modules/models/models.py +27 -15
  16. modules/overwrites.py +33 -20
  17. modules/presets.py +9 -2
  18. modules/repo.py +215 -0
  19. modules/shared.py +1 -0
  20. modules/utils.py +57 -53
  21. modules/webui.py +70 -0
  22. requirements.txt +4 -3
  23. requirements_advanced.txt +11 -0
  24. web_assets/favicon.ico +0 -0
  25. web_assets/html/appearance_switcher.html +6 -0
  26. web_assets/html/billing_info.html +9 -0
  27. web_assets/html/footer.html +1 -0
  28. web_assets/html/update.html +29 -0
  29. web_assets/javascript/ChuanhuChat.js +317 -0
  30. web_assets/javascript/chat-history.js +62 -0
  31. web_assets/javascript/external-scripts.js +2 -0
  32. web_assets/javascript/localization.js +67 -0
  33. web_assets/javascript/message-button.js +97 -0
  34. web_assets/javascript/sliders.js +22 -0
  35. web_assets/javascript/updater.js +202 -0
  36. web_assets/javascript/user-info.js +67 -0
  37. web_assets/javascript/utils.js +67 -0
  38. web_assets/stylesheet/ChuanhuChat.css +105 -0
  39. web_assets/stylesheet/chatbot.css +242 -0
  40. web_assets/stylesheet/custom-components.css +240 -0
  41. web_assets/stylesheet/markdown.css +61 -0
  42. 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("assets/custom.css", "r", encoding="utf-8") as f:
21
- customCSS = f.read()
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(css=customCSS, theme=small_and_beautiful_theme) as demo:
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="app_title")
38
- status_display = gr.Markdown(get_geoip(), elem_id="status_display")
39
- with gr.Row(elem_id="float_display"):
40
- user_info = gr.Markdown(value="getting user info...", elem_id="user_info")
41
-
42
- with gr.Row().style(equal_height=True):
 
 
 
 
 
 
 
 
43
  with gr.Column(scale=5):
44
  with gr.Row():
45
- chatbot = gr.Chatbot(label="Chuanhu Chat", elem_id="chuanhu_chatbot").style(height="100%")
46
  with gr.Row():
47
  with gr.Column(min_width=225, scale=12):
48
  user_input = gr.Textbox(
49
- elem_id="user_input_tb",
50
- show_label=False, placeholder=i18n("在这里输入")
51
- ).style(container=False)
 
52
  with gr.Column(min_width=42, scale=1):
53
- submitBtn = gr.Button(value="", variant="primary", elem_id="submit_btn")
54
- cancelBtn = gr.Button(value="", variant="secondary", visible=False, elem_id="cancel_btn")
55
  with gr.Row():
56
  emptyBtn = gr.Button(
57
- i18n("🧹 新的对话"), elem_id="empty_btn"
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="usage_display", elem_classes="insert_block")
81
  else:
82
- usageTxt = gr.Markdown(i18n("**发送消息** 或 **提交key** 以显示额度"), elem_id="usage_display", elem_classes="insert_block")
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
- ).style(container=False)
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
- ).style(container=False)
 
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
- ).style(container=False)
 
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.Column(scale=1):
145
- historyRefreshBtn = gr.Button(i18n("🔄 刷新"))
 
 
 
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
- ).style(container=True)
 
 
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.Markdown(i18n("# ⚠️ 务必谨慎更改 ⚠️\n\n如果无法使用请恢复默认设置"))
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("网络设置"), open=False, visible=False):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  # 优先展示自定义的api_host
249
  apihostTxt = gr.Textbox(
250
  show_label=True,
251
- placeholder=i18n("在这里输入API-Host..."),
252
- label="API-Host",
253
  value=config.api_host or shared.API_HOST,
254
  lines=1,
 
 
 
255
  )
256
- changeAPIURLBtn = gr.Button(i18n("🔄 切换API地址"))
257
- proxyTxt = gr.Textbox(
258
- show_label=True,
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(get_history_names, [gr.State(False), user_name], [historyFileSelectDropdown])
 
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
- change_api_host,
449
- [apihostTxt],
450
- [status_display],
451
- show_progress=True,
452
- )
453
- changeProxyBtn.click(
454
- change_proxy,
455
- [proxyTxt],
 
 
 
 
 
 
 
 
 
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
- favicon_path="./assets/favicon.ico"
 
 
 
 
 
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
- // 你的OpenAI API Key,一般必填,
3
- // 若缺省填为 "openai_api_key": "" 则必须再在图形界面中填入API Key
4
- "openai_api_key": "",
5
- // 你的xmchat API Key,与OpenAI API Key不同
6
- "xmchat_api_key": "",
7
- "language": "auto",
8
- // 如果使用代理,请取消注释下面的两行,并替换代理URL
9
- // "https_proxy": "http://127.0.0.1:1079",
10
- // "http_proxy": "http://127.0.0.1:1079",
 
 
 
 
 
 
 
 
 
 
 
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
- "two_column": false,
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
- // 如果使用自定义端口、自定义ip,请取消注释并替换对应内容
 
 
 
 
 
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("hide_history_when_not_logged_in", False)
 
 
 
 
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
- auth = json.load(f)
56
- for _ in auth:
57
- if auth[_]["username"] and auth[_]["password"]:
58
- auth_list.append((auth[_]["username"], auth[_]["password"]))
59
- else:
60
- logging.error("请检查auth.json文件中的用户名和密码!")
61
- sys.exit(1)
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
- ## 处理docker if we are running in Docker
68
  dockerflag = config.get("dockerflag", False)
69
  if os.environ.get("dockerrun") == "yes":
70
  dockerflag = True
71
 
72
- ## 处理 api-key 以及 允许的用户列表
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("OPENAI_API_BASE", config.get("openai_api_base", None))
 
101
  if api_host is not None:
102
  shared.state.set_api_host(api_host)
 
 
103
 
104
- default_chuanhu_assistant_model = config.get("default_chuanhu_assistant_model", "gpt-3.5-turbo")
 
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 = None):
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
- ## 处理log
 
121
  log_level = config.get("log_level", "INFO")
122
  logging.basicConfig(
123
  level=log_level,
124
  format="%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s",
125
  )
126
 
127
- ## 处理代理:
128
- http_proxy = config.get("http_proxy", "")
129
- https_proxy = config.get("https_proxy", "")
130
- http_proxy = os.environ.get("HTTP_PROXY", http_proxy)
131
- https_proxy = os.environ.get("HTTPS_PROXY", https_proxy)
132
 
133
  # 重置系统变量,在不需要设置的时候不设置环境变量,以免引起全局代理报错
134
  os.environ["HTTP_PROXY"] = ""
135
  os.environ["HTTPS_PROXY"] = ""
136
 
137
- local_embedding = config.get("local_embedding", False) # 是否使用本地embedding
 
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 # return new proxy
155
 
156
  # return old proxy
157
  os.environ["HTTP_PROXY"], os.environ["HTTPS_PROXY"] = old_var
158
 
159
 
160
- ## 处理advance docs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- ## 处理gradio.launch参数
 
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", encoding="utf-8") 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, metadata={"source": filepath})]
 
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, metadata={"source": filepath}))
 
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(model_name = "sentence-transformers/distiluse-base-multilingual-cased-v2")
 
119
  else:
120
  from langchain.embeddings import OpenAIEmbeddings
121
- embeddings = OpenAIEmbeddings(openai_api_base=os.environ.get("OPENAI_API_BASE", None), openai_api_key=os.environ.get("OPENAI_EMBEDDING_API_KEY", api_key))
 
 
 
 
 
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
- while not self.queue and not self.finished: # Wait for a value to be added to the queue.
 
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'<p style="font-size: smaller; color: gray;">{action_name}: {action_input}</p>'
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("stream predict not implemented, using at once predict instead")
 
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("at once predict not implemented, using stream predict instead")
 
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 = "<hr>" +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(construct_assistant(ai_reply))
 
277
  else:
278
- self.all_token_counts[-1] = total_token_count - sum(self.all_token_counts)
 
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(template=prompt_template, input_variables=["text"])
 
303
  llm = ChatOpenAI()
304
- chain = load_summarize_chain(llm, chain_type="map_reduce", return_intermediate_steps=True, map_prompt=PROMPT, combine_prompt=PROMPT)
305
- summary = chain({"input_documents": list(index.docstore.__dict__["_dict"].values())}, return_only_outputs=True)["output_text"]
 
 
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={"k":6, "score_threshold": 0.5})
327
- relevant_documents = retriever.get_relevant_documents(real_inputs)
328
- reference_results = [[d.page_content.strip("�"), os.path.basename(d.metadata["source"])] for d in relevant_documents]
 
 
 
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"<li><a href=\"{result['href']}\" target=\"_blank\">{result['title']}</a></li>\n"
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
- "用户" + f"{self.user_identifier}" + "的输入为:" + colorama.Fore.BLUE + f"{inputs}" + colorama.Style.RESET_ALL
 
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(real_inputs=inputs, use_websearch=use_websearch, files=files, reply_language=reply_language, chatbot=chatbot)
 
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
- self.api_key = new_access_key.strip()
572
- msg = i18n("API密钥更改为了") + hide_middle_chars(self.api_key)
573
- logging.info(msg)
574
- return self.api_key, msg
 
 
 
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(os.path.join(HISTORY_DIR, self.user_identifier)))).touch()
 
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, self.history, chatbot, self.user_identifier)
 
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(HISTORY_DIR, user_name, filename)
 
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(history_file_path, self.user_identifier)
 
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}.&nbsp;{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
- <b>""" + i18n("本月使用金额") + f"""</b>
98
- <div class="progress-bar">
99
- <div class="progress" style="width: {usage_percent}%;">
100
- <span class="progress-text">{usage_percent}%</span>
101
- </div>
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 {self.api_key}",
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
- logging.error(e)
 
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
- with open("./assets/custom.js", "r", encoding="utf-8") as f, \
75
- open("./assets/external-scripts.js", "r", encoding="utf-8") as f1:
76
- customJS = f.read()
77
- externalScripts = f1.read()
78
-
79
-
80
- def reload_javascript():
81
- print("Reloading javascript...")
82
- js = f'<script>{customJS}</script><script async>{externalScripts}</script>'
83
- # if render_latex:
84
- # js += """\"""
85
- def template_response(*args, **kwargs):
86
- res = GradioTemplateResponseOriginal(*args, **kwargs)
87
- res.body = res.body.replace(b'</html>', f'{js}</html>'.encode("utf8"))
88
- res.init_headers()
89
- return res
90
-
91
- gr.routes.templates.TemplateResponse = template_response
92
-
93
- GradioTemplateResponseOriginal = gr.routes.templates.TemplateResponse
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-qe",
 
 
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="wihte",
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">{result}\n</div>'
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
- ' ': '&nbsp;',
240
  '_': '&#95;',
241
  '*': '&#42;',
242
  '[': '&#91;',
@@ -253,8 +280,12 @@ def escape_markdown(text):
253
  '`': '&#96;',
254
  '>': '&#62;',
255
  '<': '&#60;',
256
- '|': '&#124;'
 
 
 
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
- def run(command, desc=None, errdesc=None, custom_env=None, live=False):
513
- if desc is not None:
514
- print(desc)
515
- if live:
516
- result = subprocess.run(command, shell=True, env=os.environ if custom_env is None else custom_env)
517
- if result.returncode != 0:
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
- commit_info = "unknown \U0001F615"
546
- return f"""
547
- Python: <span title="{sys.version}">{python_version}</span>
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
+ # ' ': '&nbsp;',
267
  '_': '&#95;',
268
  '*': '&#42;',
269
  '[': '&#91;',
 
280
  '`': '&#96;',
281
  '>': '&#62;',
282
  '<': '&#60;',
283
+ '|': '&#124;',
284
+ '$': '&#36;',
285
+ ':': '&#58;',
286
+ '\n': '<br>',
287
  }
288
+ text = text.replace(' ', '&nbsp;&nbsp;&nbsp;&nbsp;')
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.33.1
2
- gradio_client==0.2.5
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
+ }