JohnSmith9982 commited on
Commit
79eae90
1 Parent(s): b74ce33

Upload 97 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. ChuanhuChatbot.py +555 -307
  2. config_example.json +15 -8
  3. locale/en_US.json +126 -72
  4. locale/extract_locale.py +132 -20
  5. locale/ja_JP.json +126 -72
  6. locale/ko_KR.json +126 -74
  7. locale/ru_RU.json +141 -0
  8. locale/sv_SE.json +141 -0
  9. locale/vi_VN.json +141 -0
  10. locale/zh_CN.json +1 -0
  11. modules/config.py +26 -6
  12. modules/index_func.py +6 -3
  13. modules/models/Azure.py +18 -0
  14. modules/models/ChatGLM.py +84 -0
  15. modules/models/ChuanhuAgent.py +18 -2
  16. modules/models/Claude.py +55 -0
  17. modules/models/GooglePaLM.py +29 -0
  18. modules/models/LLaMA.py +126 -0
  19. modules/models/OpenAI.py +279 -0
  20. modules/models/OpenAIInstruct.py +27 -0
  21. modules/models/OpenAIVision.py +328 -0
  22. modules/models/Qwen.py +57 -0
  23. modules/models/XMChat.py +149 -0
  24. modules/models/base_model.py +178 -79
  25. modules/models/midjourney.py +6 -7
  26. modules/models/models.py +49 -543
  27. modules/models/spark.py +166 -0
  28. modules/overwrites.py +29 -26
  29. modules/presets.py +97 -31
  30. modules/repo.py +129 -42
  31. modules/shared.py +7 -5
  32. modules/utils.py +105 -62
  33. modules/webui.py +14 -1
  34. modules/webui_locale.py +9 -2
  35. readme/README_en.md +86 -36
  36. readme/README_ja.md +76 -28
  37. readme/README_ru.md +186 -0
  38. requirements.txt +10 -5
  39. requirements_advanced.txt +5 -4
  40. templates/6 Russian Prompts.json +0 -0
  41. web_assets/.DS_Store +0 -0
  42. web_assets/html/chatbot_header_btn.html +72 -0
  43. web_assets/html/chatbot_more.html +72 -0
  44. web_assets/html/close_btn.html +5 -0
  45. web_assets/html/func_nav.html +78 -0
  46. web_assets/html/header_title.html +20 -0
  47. web_assets/html/web_config.html +21 -0
  48. web_assets/icon/any-icon-512.png +0 -0
  49. web_assets/icon/mask-icon-512.png +0 -0
  50. web_assets/javascript/ChuanhuChat.js +231 -99
ChuanhuChatbot.py CHANGED
@@ -5,18 +5,18 @@ logging.basicConfig(
5
  format="%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s",
6
  )
7
 
8
- import colorama
 
 
 
 
 
 
 
 
9
  import gradio as gr
 
10
 
11
- from modules import config
12
- from modules.config import *
13
- from modules.utils import *
14
- from modules.presets import *
15
- from modules.overwrites import *
16
- from modules.webui import *
17
- from modules.repo import *
18
- from modules.train_func import *
19
- from modules.models.models import get_model
20
 
21
  logging.getLogger("httpx").setLevel(logging.WARNING)
22
 
@@ -26,25 +26,28 @@ gr.Chatbot.postprocess = postprocess
26
  # with open("web_assets/css/ChuanhuChat.css", "r", encoding="utf-8") as f:
27
  # ChuanhuChatCSS = f.read()
28
 
 
29
  def create_new_model():
30
- return get_model(model_name = MODELS[DEFAULT_MODEL], access_key = my_api_key)[0]
 
31
 
32
  with gr.Blocks(theme=small_and_beautiful_theme) as demo:
33
- user_name = gr.State("")
34
- promptTemplates = gr.State(load_template(get_template_names(plain=True)[0], mode=2))
35
  user_question = gr.State("")
36
- assert type(my_api_key)==str
37
  user_api_key = gr.State(my_api_key)
38
- current_model = gr.State(create_new_model)
39
 
40
  topic = gr.State(i18n("未命名对话历史记录"))
41
 
42
- with gr.Row():
43
- gr.HTML(CHUANHU_TITLE, elem_id="app-title")
 
44
  status_display = gr.Markdown(get_geoip(), elem_id="status-display")
45
  with gr.Row(elem_id="float-display"):
46
- user_info = gr.Markdown(value="getting user info...", elem_id="user-info")
47
- config_info = gr.HTML(get_html("config_info.html").format(bot_avatar=config.bot_avatar, user_avatar=config.user_avatar), visible=False, elem_id="config-info")
48
  update_info = gr.HTML(get_html("update.html").format(
49
  current_version=repo_tag_html(),
50
  version_time=version_time(),
@@ -52,248 +55,339 @@ with gr.Blocks(theme=small_and_beautiful_theme) as demo:
52
  update_btn=i18n("更新"),
53
  seenew_btn=i18n("详情"),
54
  ok_btn=i18n("好"),
55
- ), visible=check_update)
56
-
57
- with gr.Row(equal_height=True):
58
- with gr.Column(scale=5):
59
- with gr.Row():
60
- chatbot = gr.Chatbot(label="Chuanhu Chat", elem_id="chuanhu-chatbot", latex_delimiters=latex_delimiters_set, height=700)
61
- with gr.Row():
62
- with gr.Column(min_width=225, scale=12):
63
- user_input = gr.Textbox(
64
- elem_id="user-input-tb",
65
- show_label=False, placeholder=i18n("在这里输入"),
66
- container=False
67
- )
68
- with gr.Column(min_width=42, scale=1):
69
- submitBtn = gr.Button(value="", variant="primary", elem_id="submit-btn")
70
- cancelBtn = gr.Button(value="", variant="secondary", visible=False, elem_id="cancel-btn")
71
- with gr.Row(elem_id="chatbot-buttons"):
72
- with gr.Column(min_width=120, scale=1):
73
- emptyBtn = gr.Button(
74
- i18n("🧹 新的对话"), elem_id="empty-btn"
75
- )
76
- with gr.Column(min_width=120, scale=1):
77
- retryBtn = gr.Button(i18n("🔄 重新生成"))
78
- with gr.Column(min_width=120, scale=1):
79
- delFirstBtn = gr.Button(i18n("🗑️ 删除最旧对话"))
80
- with gr.Column(min_width=120, scale=1):
81
- delLastBtn = gr.Button(i18n("🗑️ 删除最新对话"))
82
- with gr.Row(visible=False) as like_dislike_area:
83
- with gr.Column(min_width=20, scale=1):
84
- likeBtn = gr.Button(i18n("👍"))
85
- with gr.Column(min_width=20, scale=1):
86
- dislikeBtn = gr.Button(i18n("👎"))
87
-
88
- with gr.Column():
89
- with gr.Column(min_width=50, scale=1):
90
- with gr.Tab(label=i18n("模型")):
91
- keyTxt = gr.Textbox(
92
- show_label=True,
93
- placeholder=f"Your API-key...",
94
- value=hide_middle_chars(user_api_key.value),
95
- type="password",
96
- visible=not HIDE_MY_KEY,
97
- label="API-Key",
98
- )
99
- if multi_api_key:
100
- usageTxt = gr.Markdown(i18n("多账号模式已开启,无需输入key,可直接开始对话"), elem_id="usage-display", elem_classes="insert-block", visible=show_api_billing)
101
- else:
102
- usageTxt = gr.Markdown(i18n("**发送消息** 或 **提交key** 以显示额度"), elem_id="usage-display", elem_classes="insert-block", visible=show_api_billing)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  model_select_dropdown = gr.Dropdown(
104
- label=i18n("选择模型"), choices=MODELS, multiselect=False, value=MODELS[DEFAULT_MODEL], interactive=True
 
105
  )
106
  lora_select_dropdown = gr.Dropdown(
107
- label=i18n("选择LoRA模型"), choices=[], multiselect=False, interactive=True, visible=False
 
108
  )
109
- with gr.Row():
110
- single_turn_checkbox = gr.Checkbox(label=i18n("单轮对话"), value=False, elem_classes="switch-checkbox")
111
- use_websearch_checkbox = gr.Checkbox(label=i18n("使用在线搜索"), value=False, elem_classes="switch-checkbox")
112
- language_select_dropdown = gr.Dropdown(
113
- label=i18n("选择回复语言(针对搜索&索引功能)"),
114
- choices=REPLY_LANGUAGES,
115
- multiselect=False,
116
- value=REPLY_LANGUAGES[0],
 
 
 
 
 
117
  )
118
- index_files = gr.Files(label=i18n("上传"), type="file", elem_id="upload-index-file")
119
- two_column = gr.Checkbox(label=i18n("双栏pdf"), value=advance_docs["pdf"].get("two_column", False))
120
- summarize_btn = gr.Button(i18n("总结"))
121
- # TODO: 公式ocr
122
- # formula_ocr = gr.Checkbox(label=i18n("识别公式"), value=advance_docs["pdf"].get("formula_ocr", False))
123
-
124
- with gr.Tab(label="Prompt"):
125
- systemPromptTxt = gr.Textbox(
126
- show_label=True,
127
- placeholder=i18n("在这里输入System Prompt..."),
128
- label="System prompt",
129
- value=INITIAL_SYSTEM_PROMPT,
130
- lines=10
131
- )
132
- with gr.Accordion(label=i18n("加载Prompt模板"), open=True):
133
- with gr.Column():
134
- with gr.Row():
135
- with gr.Column(scale=6):
136
- templateFileSelectDropdown = gr.Dropdown(
137
- label=i18n("选择Prompt模板集合文件"),
138
- choices=get_template_names(plain=True),
139
- multiselect=False,
140
- value=get_template_names(plain=True)[0],
141
- container=False,
142
- )
143
- with gr.Column(scale=1):
144
- templateRefreshBtn = gr.Button(i18n("🔄 刷新"))
145
- with gr.Row():
146
- with gr.Column():
147
- templateSelectDropdown = gr.Dropdown(
148
- label=i18n("从Prompt模板中加载"),
149
- choices=load_template(
150
- get_template_names(plain=True)[0], mode=1
151
- ),
152
- multiselect=False,
153
- container=False,
154
- )
155
-
156
- with gr.Tab(label=i18n("保存/加载")):
157
- with gr.Accordion(label=i18n("保存/加载对话历史记录"), open=True):
158
- with gr.Column():
159
- with gr.Row():
160
- with gr.Column(scale=6):
161
- historyFileSelectDropdown = gr.Dropdown(
162
- label=i18n("从列表中加载对话"),
163
- choices=get_history_names(plain=True),
164
- multiselect=False,
165
- container=False,
166
  )
167
- with gr.Row():
168
- with gr.Column(min_width=42, scale=1):
169
- historyRefreshBtn = gr.Button(i18n("🔄 刷新"))
170
- with gr.Column(min_width=42, scale=1):
171
- historyDeleteBtn = gr.Button(i18n("🗑️ 删除"))
172
- with gr.Row():
173
- with gr.Column(scale=6):
174
- saveFileName = gr.Textbox(
175
- show_label=True,
176
- placeholder=i18n("设置文件名: 默认为.json,可选为.md"),
177
- label=i18n("设置保存文件名"),
178
- value=i18n("对话历史记录"),
179
- elem_classes="no-container"
180
- # container=False,
181
- )
182
- with gr.Column(scale=1):
183
- saveHistoryBtn = gr.Button(i18n("💾 保存对话"))
184
- exportMarkdownBtn = gr.Button(i18n("📝 导出为Markdown"))
185
- gr.Markdown(i18n("默认保存于history文件夹"))
186
- with gr.Row():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  with gr.Column():
188
- downloadFile = gr.File(interactive=True)
189
-
190
- with gr.Tab(label=i18n("微调")):
191
- openai_train_status = gr.Markdown(label=i18n("训练状态"), value=i18n("在这里[查看使用介绍](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B#%E5%BE%AE%E8%B0%83-gpt-35)"))
192
-
193
- with gr.Tab(label=i18n("准备数据集")):
194
- dataset_preview_json = gr.JSON(label=i18n("数据集预览"), readonly=True)
195
- dataset_selection = gr.Files(label = i18n("选择数据集"), file_types=[".xlsx", ".jsonl"], file_count="single")
196
- upload_to_openai_btn = gr.Button(i18n("上传到OpenAI"), variant="primary", interactive=False)
197
-
198
- with gr.Tab(label=i18n("训练")):
199
- openai_ft_file_id = gr.Textbox(label=i18n("文件ID"), value="", lines=1, placeholder=i18n("上传到 OpenAI 后自动填充"))
200
- openai_ft_suffix = gr.Textbox(label=i18n("模型名称后缀"), value="", lines=1, placeholder=i18n("可选,用于区分不同的模型"))
201
- openai_train_epoch_slider = gr.Slider(label=i18n("训练轮数(Epochs)"), minimum=1, maximum=100, value=3, step=1, interactive=True)
202
- openai_start_train_btn = gr.Button(i18n("开始训练"), variant="primary", interactive=False)
203
-
204
- with gr.Tab(label=i18n("状态")):
205
- openai_status_refresh_btn = gr.Button(i18n("刷新状态"))
206
- openai_cancel_all_jobs_btn = gr.Button(i18n("取消所有任务"))
207
- add_to_models_btn = gr.Button(i18n("添加训练好的模型到模型列表"), interactive=False)
208
-
209
- with gr.Tab(label=i18n("高级")):
210
- gr.HTML(get_html("appearance_switcher.html").format(label=i18n("切换亮暗色主题")), elem_classes="insert-block")
211
- use_streaming_checkbox = gr.Checkbox(
212
- label=i18n("实时传输回答"), value=True, visible=ENABLE_STREAMING_OPTION, elem_classes="switch-checkbox"
213
- )
214
- checkUpdateBtn = gr.Button(i18n("🔄 检查更新..."), visible=check_update)
215
- gr.Markdown(i18n("# ⚠️ 务必谨慎更改 ⚠️"), elem_id="advanced-warning")
216
- with gr.Accordion(i18n("参数"), open=False):
217
- temperature_slider = gr.Slider(
218
- minimum=-0,
219
- maximum=2.0,
220
- value=1.0,
221
- step=0.1,
222
- interactive=True,
223
- label="temperature",
224
- )
225
- top_p_slider = gr.Slider(
226
- minimum=-0,
227
- maximum=1.0,
228
- value=1.0,
229
- step=0.05,
230
- interactive=True,
231
- label="top-p",
232
- )
233
- n_choices_slider = gr.Slider(
234
- minimum=1,
235
- maximum=10,
236
- value=1,
237
- step=1,
238
- interactive=True,
239
- label="n choices",
240
- )
241
- stop_sequence_txt = gr.Textbox(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  show_label=True,
243
- placeholder=i18n("停止符,用英文逗号隔开..."),
244
- label="stop",
245
- value="",
246
- lines=1,
 
247
  )
248
- max_context_length_slider = gr.Slider(
249
- minimum=1,
250
- maximum=32768,
251
- value=2000,
252
- step=1,
253
- interactive=True,
254
- label="max context",
 
 
 
 
 
 
 
 
 
 
 
 
255
  )
256
- max_generation_slider = gr.Slider(
257
- minimum=1,
258
- maximum=32768,
259
- value=1000,
260
- step=1,
261
- interactive=True,
262
- label="max generations",
263
- )
264
- presence_penalty_slider = gr.Slider(
265
- minimum=-2.0,
266
- maximum=2.0,
267
- value=0.0,
268
- step=0.01,
269
- interactive=True,
270
- label="presence penalty",
271
  )
272
- frequency_penalty_slider = gr.Slider(
273
- minimum=-2.0,
274
- maximum=2.0,
275
- value=0.0,
276
- step=0.01,
277
  interactive=True,
278
- label="frequency penalty",
279
- )
280
- logit_bias_txt = gr.Textbox(
281
- show_label=True,
282
- placeholder=f"word:likelihood",
283
- label="logit bias",
284
- value="",
285
- lines=1,
286
- )
287
- user_identifier_txt = gr.Textbox(
288
- show_label=True,
289
- placeholder=i18n("用于定位滥用行为"),
290
- label=i18n("用户名"),
291
- value=user_name.value,
292
- lines=1,
293
  )
 
 
 
294
 
295
- with gr.Accordion(i18n("网络参数"), open=False):
296
- gr.Markdown(i18n("---\n⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置"), elem_id="netsetting-warning")
 
297
  default_btn = gr.Button(i18n("🔙 恢复默认网络设置"))
298
  # 网络代理
299
  proxyTxt = gr.Textbox(
@@ -319,25 +413,99 @@ with gr.Blocks(theme=small_and_beautiful_theme) as demo:
319
  # container=False,
320
  elem_classes="view-only-textbox no-container",
321
  )
322
- # changeAPIURLBtn = gr.Button(i18n("🔄 切换API地址"))
323
- updateChuanhuBtn = gr.Button(visible=False, elem_classes="invisible-btn", elem_id="update-chuanhu-btn")
324
 
325
-
326
- gr.Markdown(CHUANHU_DESCRIPTION, elem_id="description")
327
- gr.HTML(get_html("footer.html").format(versions=versions_html()), elem_id="footer")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
 
329
  # https://github.com/gradio-app/gradio/pull/3296
 
330
  def create_greeting(request: gr.Request):
331
- if hasattr(request, "username") and request.username: # is not None or is not ""
332
  logging.info(f"Get User Name: {request.username}")
333
- user_info, user_name = gr.Markdown.update(value=f"User: {request.username}"), request.username
 
334
  else:
335
- user_info, user_name = gr.Markdown.update(value=f"", visible=False), ""
336
- current_model = get_model(model_name = MODELS[DEFAULT_MODEL], access_key = my_api_key)[0]
 
 
337
  current_model.set_user_identifier(user_name)
338
- chatbot = gr.Chatbot.update(label=MODELS[DEFAULT_MODEL])
339
- return user_info, user_name, current_model, toggle_like_btn_visibility(DEFAULT_MODEL), *current_model.auto_load(), get_history_names(False, user_name), chatbot
340
- demo.load(create_greeting, inputs=None, outputs=[user_info, user_name, current_model, like_dislike_area, systemPromptTxt, chatbot, historyFileSelectDropdown, chatbot], api_name="load")
 
 
 
 
 
 
341
  chatgpt_predict_args = dict(
342
  fn=predict,
343
  inputs=[
@@ -369,42 +537,58 @@ with gr.Blocks(theme=small_and_beautiful_theme) as demo:
369
  )
370
 
371
  transfer_input_args = dict(
372
- fn=transfer_input, inputs=[user_input], outputs=[user_question, user_input, submitBtn, cancelBtn], show_progress=True
 
373
  )
374
 
375
  get_usage_args = dict(
376
- fn=billing_info, inputs=[current_model], outputs=[usageTxt], show_progress=False
 
377
  )
378
 
379
  load_history_from_file_args = dict(
380
  fn=load_chat_history,
381
- inputs=[current_model, historyFileSelectDropdown, user_name],
382
  outputs=[saveFileName, systemPromptTxt, chatbot]
383
  )
384
 
385
  refresh_history_args = dict(
386
- fn=get_history_names, inputs=[gr.State(False), user_name], outputs=[historyFileSelectDropdown]
387
  )
388
 
 
 
 
 
 
 
389
 
390
  # Chatbot
391
  cancelBtn.click(interrupt, [current_model], [])
392
 
393
- user_input.submit(**transfer_input_args).then(**chatgpt_predict_args).then(**end_outputing_args)
 
394
  user_input.submit(**get_usage_args)
395
 
396
- submitBtn.click(**transfer_input_args).then(**chatgpt_predict_args, api_name="predict").then(**end_outputing_args)
 
 
 
397
  submitBtn.click(**get_usage_args)
398
 
399
- index_files.change(handle_file_upload, [current_model, index_files, chatbot, language_select_dropdown], [index_files, chatbot, status_display])
400
- summarize_btn.click(handle_summarize_index, [current_model, index_files, chatbot, language_select_dropdown], [chatbot, status_display])
 
 
 
 
401
 
402
  emptyBtn.click(
403
  reset,
404
- inputs=[current_model],
405
- outputs=[chatbot, status_display],
406
  show_progress=True,
407
- _js='clearChatbot',
408
  )
409
 
410
  retryBtn.click(**start_outputing_args).then(
@@ -452,17 +636,24 @@ with gr.Blocks(theme=small_and_beautiful_theme) as demo:
452
  two_column.change(update_doc_config, [two_column], None)
453
 
454
  # LLM Models
455
- keyTxt.change(set_key, [current_model, keyTxt], [user_api_key, status_display], api_name="set_key").then(**get_usage_args)
 
456
  keyTxt.submit(**get_usage_args)
457
- single_turn_checkbox.change(set_single_turn, [current_model, single_turn_checkbox], None)
458
- model_select_dropdown.change(get_model, [model_select_dropdown, lora_select_dropdown, user_api_key, temperature_slider, top_p_slider, systemPromptTxt, user_name], [current_model, status_display, chatbot, lora_select_dropdown, user_api_key, keyTxt], show_progress=True, api_name="get_model")
459
- model_select_dropdown.change(toggle_like_btn_visibility, [model_select_dropdown], [like_dislike_area], show_progress=False)
460
- lora_select_dropdown.change(get_model, [model_select_dropdown, lora_select_dropdown, user_api_key, temperature_slider, top_p_slider, systemPromptTxt, user_name], [current_model, status_display, chatbot], show_progress=True)
 
 
 
 
461
 
462
  # Template
463
- systemPromptTxt.change(set_system_prompt, [current_model, systemPromptTxt], None)
464
- templateRefreshBtn.click(get_template_names, None, [templateFileSelectDropdown])
465
- templateFileSelectDropdown.change(
 
 
466
  load_template,
467
  [templateFileSelectDropdown],
468
  [promptTemplates, templateSelectDropdown],
@@ -476,47 +667,80 @@ with gr.Blocks(theme=small_and_beautiful_theme) as demo:
476
  )
477
 
478
  # S&L
479
- saveHistoryBtn.click(
480
- save_chat_history,
481
  [current_model, saveFileName, chatbot, user_name],
482
- downloadFile,
483
  show_progress=True,
 
484
  )
485
- saveHistoryBtn.click(get_history_names, [gr.State(False), user_name], [historyFileSelectDropdown])
486
  exportMarkdownBtn.click(
487
  export_markdown,
488
  [current_model, saveFileName, chatbot, user_name],
489
- downloadFile,
490
  show_progress=True,
491
  )
492
  historyRefreshBtn.click(**refresh_history_args)
493
- historyDeleteBtn.click(delete_chat_history, [current_model, historyFileSelectDropdown, user_name], [status_display, historyFileSelectDropdown, chatbot], _js='(a,b,c)=>{return showConfirmationDialog(a, b, c);}')
494
- historyFileSelectDropdown.change(**load_history_from_file_args)
495
- downloadFile.change(upload_chat_history, [current_model, downloadFile, user_name], [saveFileName, systemPromptTxt, chatbot])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496
 
497
  # Train
498
- dataset_selection.upload(handle_dataset_selection, dataset_selection, [dataset_preview_json, upload_to_openai_btn, openai_train_status])
499
- dataset_selection.clear(handle_dataset_clear, [], [dataset_preview_json, upload_to_openai_btn])
500
- upload_to_openai_btn.click(upload_to_openai, [dataset_selection], [openai_ft_file_id, openai_train_status], show_progress=True)
501
-
502
- openai_ft_file_id.change(lambda x: gr.update(interactive=True) if len(x) > 0 else gr.update(interactive=False), [openai_ft_file_id], [openai_start_train_btn])
503
- openai_start_train_btn.click(start_training, [openai_ft_file_id, openai_ft_suffix, openai_train_epoch_slider], [openai_train_status])
504
-
505
- openai_status_refresh_btn.click(get_training_status, [], [openai_train_status, add_to_models_btn])
506
- add_to_models_btn.click(add_to_models, [], [model_select_dropdown, openai_train_status], show_progress=True)
507
- openai_cancel_all_jobs_btn.click(cancel_all_jobs, [], [openai_train_status], show_progress=True)
 
 
 
 
 
 
 
 
508
 
509
  # Advanced
510
- max_context_length_slider.change(set_token_upper_limit, [current_model, max_context_length_slider], None)
511
- temperature_slider.change(set_temperature, [current_model, temperature_slider], None)
 
 
512
  top_p_slider.change(set_top_p, [current_model, top_p_slider], None)
513
- n_choices_slider.change(set_n_choices, [current_model, n_choices_slider], None)
514
- stop_sequence_txt.change(set_stop_sequence, [current_model, stop_sequence_txt], None)
515
- max_generation_slider.change(set_max_tokens, [current_model, max_generation_slider], None)
516
- presence_penalty_slider.change(set_presence_penalty, [current_model, presence_penalty_slider], None)
517
- frequency_penalty_slider.change(set_frequency_penalty, [current_model, frequency_penalty_slider], None)
518
- logit_bias_txt.change(set_logit_bias, [current_model, logit_bias_txt], None)
519
- user_identifier_txt.change(set_user_identifier, [current_model, user_identifier_txt], None)
 
 
 
 
 
 
 
520
 
521
  default_btn.click(
522
  reset_default, [], [apihostTxt, proxyTxt, status_display], show_progress=True
@@ -533,7 +757,7 @@ with gr.Blocks(theme=small_and_beautiful_theme) as demo:
533
  # [status_display],
534
  # show_progress=True,
535
  # )
536
- checkUpdateBtn.click(fn=None, _js='manualCheckUpdate')
537
 
538
  # Invisible elements
539
  updateChuanhuBtn.click(
@@ -542,6 +766,25 @@ with gr.Blocks(theme=small_and_beautiful_theme) as demo:
542
  [status_display],
543
  show_progress=True,
544
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
 
546
  logging.info(
547
  colorama.Back.GREEN
@@ -554,6 +797,11 @@ demo.title = i18n("川虎Chat 🚀")
554
  if __name__ == "__main__":
555
  reload_javascript()
556
  demo.queue(concurrency_count=CONCURRENT_COUNT).launch(
557
- blocked_paths=["config.json"],
 
 
 
 
558
  favicon_path="./web_assets/favicon.ico",
 
559
  )
 
5
  format="%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s",
6
  )
7
 
8
+ from modules.models.models import get_model
9
+ from modules.train_func import *
10
+ from modules.repo import *
11
+ from modules.webui import *
12
+ from modules.overwrites import *
13
+ from modules.presets import *
14
+ from modules.utils import *
15
+ from modules.config import *
16
+ from modules import config
17
  import gradio as gr
18
+ import colorama
19
 
 
 
 
 
 
 
 
 
 
20
 
21
  logging.getLogger("httpx").setLevel(logging.WARNING)
22
 
 
26
  # with open("web_assets/css/ChuanhuChat.css", "r", encoding="utf-8") as f:
27
  # ChuanhuChatCSS = f.read()
28
 
29
+
30
  def create_new_model():
31
+ return get_model(model_name=MODELS[DEFAULT_MODEL], access_key=my_api_key)[0]
32
+
33
 
34
  with gr.Blocks(theme=small_and_beautiful_theme) as demo:
35
+ user_name = gr.Textbox("", visible=False)
36
+ promptTemplates = gr.State(load_template(get_template_names()[0], mode=2))
37
  user_question = gr.State("")
38
+ assert type(my_api_key) == str
39
  user_api_key = gr.State(my_api_key)
40
+ current_model = gr.State()
41
 
42
  topic = gr.State(i18n("未命名对话历史记录"))
43
 
44
+ with gr.Row(elem_id="chuanhu-header"):
45
+ gr.HTML(get_html("header_title.html").format(
46
+ app_title=CHUANHU_TITLE), elem_id="app-title")
47
  status_display = gr.Markdown(get_geoip(), elem_id="status-display")
48
  with gr.Row(elem_id="float-display"):
49
+ user_info = gr.Markdown(
50
+ value="getting user info...", elem_id="user-info")
51
  update_info = gr.HTML(get_html("update.html").format(
52
  current_version=repo_tag_html(),
53
  version_time=version_time(),
 
55
  update_btn=i18n("更新"),
56
  seenew_btn=i18n("详情"),
57
  ok_btn=i18n("好"),
58
+ ), visible=check_update)
59
+
60
+ with gr.Row(equal_height=True, elem_id="chuanhu-body"):
61
+
62
+ with gr.Column(elem_id="menu-area"):
63
+ with gr.Column(elem_id="chuanhu-history"):
64
+ with gr.Box():
65
+ with gr.Row(elem_id="chuanhu-history-header"):
66
+ with gr.Row(elem_id="chuanhu-history-search-row"):
67
+ with gr.Column(min_width=150, scale=2):
68
+ historySearchTextbox = gr.Textbox(show_label=False, container=False, placeholder=i18n(
69
+ "搜索(支持正则)..."), lines=1, elem_id="history-search-tb")
70
+ with gr.Column(min_width=52, scale=1, elem_id="gr-history-header-btns"):
71
+ uploadFileBtn = gr.UploadButton(
72
+ interactive=True, label="", file_types=[".json"], elem_id="gr-history-upload-btn")
73
+ historyRefreshBtn = gr.Button("", elem_id="gr-history-refresh-btn")
74
+
75
+
76
+ with gr.Row(elem_id="chuanhu-history-body"):
77
+ with gr.Column(scale=6, elem_id="history-select-wrap"):
78
+ historySelectList = gr.Radio(
79
+ label=i18n("从列表中加载对话"),
80
+ choices=get_history_names(),
81
+ value=get_first_history_name(),
82
+ # multiselect=False,
83
+ container=False,
84
+ elem_id="history-select-dropdown"
85
+ )
86
+ with gr.Row(visible=False):
87
+ with gr.Column(min_width=42, scale=1):
88
+ historyDeleteBtn = gr.Button(
89
+ "🗑️", elem_id="gr-history-delete-btn")
90
+ with gr.Column(min_width=42, scale=1):
91
+ historyDownloadBtn = gr.Button(
92
+ "⏬", elem_id="gr-history-download-btn")
93
+ with gr.Column(min_width=42, scale=1):
94
+ historyMarkdownDownloadBtn = gr.Button(
95
+ "⤵️", elem_id="gr-history-mardown-download-btn")
96
+ with gr.Row(visible=False):
97
+ with gr.Column(scale=6):
98
+ saveFileName = gr.Textbox(
99
+ show_label=True,
100
+ placeholder=i18n("设置文件名: 默认为.json,可选为.md"),
101
+ label=i18n("设置保存文件名"),
102
+ value=i18n("对话历史记录"),
103
+ elem_classes="no-container"
104
+ # container=False,
105
+ )
106
+ with gr.Column(scale=1):
107
+ renameHistoryBtn = gr.Button(
108
+ i18n("💾 保存对话"), elem_id="gr-history-save-btn")
109
+ exportMarkdownBtn = gr.Button(
110
+ i18n("📝 导出为 Markdown"), elem_id="gr-markdown-export-btn")
111
+
112
+ with gr.Column(elem_id="chuanhu-menu-footer"):
113
+ with gr.Row(elem_id="chuanhu-func-nav"):
114
+ gr.HTML(get_html("func_nav.html"))
115
+ # gr.HTML(get_html("footer.html").format(versions=versions_html()), elem_id="footer")
116
+ # gr.Markdown(CHUANHU_DESCRIPTION, elem_id="chuanhu-author")
117
+
118
+ with gr.Column(elem_id="chuanhu-area", scale=5):
119
+ with gr.Column(elem_id="chatbot-area"):
120
+ with gr.Row(elem_id="chatbot-header"):
121
  model_select_dropdown = gr.Dropdown(
122
+ label=i18n("选择模型"), choices=MODELS, multiselect=False, value=MODELS[DEFAULT_MODEL], interactive=True,
123
+ show_label=False, container=False, elem_id="model-select-dropdown"
124
  )
125
  lora_select_dropdown = gr.Dropdown(
126
+ label=i18n("选择LoRA模型"), choices=[], multiselect=False, interactive=True, visible=False,
127
+ container=False,
128
  )
129
+ gr.HTML(get_html("chatbot_header_btn.html").format(
130
+ json_label=i18n("历史记录(JSON)"),
131
+ md_label=i18n("导出为 Markdown")
132
+ ), elem_id="chatbot-header-btn-bar")
133
+ with gr.Row():
134
+ chatbot = gr.Chatbot(
135
+ label="Chuanhu Chat",
136
+ elem_id="chuanhu-chatbot",
137
+ latex_delimiters=latex_delimiters_set,
138
+ # height=700,
139
+ show_label=False,
140
+ avatar_images=[config.user_avatar, config.bot_avatar],
141
+ show_share_button=False,
142
  )
143
+ with gr.Row(elem_id="chatbot-footer"):
144
+ with gr.Box(elem_id="chatbot-input-box"):
145
+ with gr.Row(elem_id="chatbot-input-row"):
146
+ gr.HTML(get_html("chatbot_more.html").format(
147
+ single_turn_label=i18n("单轮对话"),
148
+ websearch_label=i18n("在线搜索"),
149
+ upload_file_label=i18n("上传文件"),
150
+ uploaded_files_label=i18n("知识库文件"),
151
+ uploaded_files_tip=i18n("在工具箱中管理知识库文件")
152
+ ))
153
+ with gr.Row(elem_id="chatbot-input-tb-row"):
154
+ with gr.Column(min_width=225, scale=12):
155
+ user_input = gr.Textbox(
156
+ elem_id="user-input-tb",
157
+ show_label=False,
158
+ placeholder=i18n("在这里输入"),
159
+ elem_classes="no-container",
160
+ max_lines=5,
161
+ # container=False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  )
163
+ with gr.Column(min_width=42, scale=1, elem_id="chatbot-ctrl-btns"):
164
+ submitBtn = gr.Button(
165
+ value="", variant="primary", elem_id="submit-btn")
166
+ cancelBtn = gr.Button(
167
+ value="", variant="secondary", visible=False, elem_id="cancel-btn")
168
+ # Note: Buttons below are set invisible in UI. But they are used in JS.
169
+ with gr.Row(elem_id="chatbot-buttons", visible=False):
170
+ with gr.Column(min_width=120, scale=1):
171
+ emptyBtn = gr.Button(
172
+ i18n("🧹 新的对话"), elem_id="empty-btn"
173
+ )
174
+ with gr.Column(min_width=120, scale=1):
175
+ retryBtn = gr.Button(
176
+ i18n("🔄 重新生成"), elem_id="gr-retry-btn")
177
+ with gr.Column(min_width=120, scale=1):
178
+ delFirstBtn = gr.Button(i18n("🗑️ 删除最旧对话"))
179
+ with gr.Column(min_width=120, scale=1):
180
+ delLastBtn = gr.Button(
181
+ i18n("🗑️ 删除最新对话"), elem_id="gr-dellast-btn")
182
+ with gr.Row(visible=False) as like_dislike_area:
183
+ with gr.Column(min_width=20, scale=1):
184
+ likeBtn = gr.Button(
185
+ "👍", elem_id="gr-like-btn")
186
+ with gr.Column(min_width=20, scale=1):
187
+ dislikeBtn = gr.Button(
188
+ "👎", elem_id="gr-dislike-btn")
189
+
190
+ with gr.Column(elem_id="toolbox-area", scale=1):
191
+ # For CSS setting, there is an extra box. Don't remove it.
192
+ with gr.Box(elem_id="chuanhu-toolbox"):
193
+ with gr.Row():
194
+ gr.Markdown("## "+i18n("工具箱"))
195
+ gr.HTML(get_html("close_btn.html").format(
196
+ obj="toolbox"), elem_classes="close-btn")
197
+ with gr.Tabs(elem_id="chuanhu-toolbox-tabs"):
198
+ with gr.Tab(label=i18n("对话")):
199
+ with gr.Accordion(label="Prompt", open=True):
200
+ systemPromptTxt = gr.Textbox(
201
+ show_label=True,
202
+ placeholder=i18n("在这里输入System Prompt..."),
203
+ label="System prompt",
204
+ value=INITIAL_SYSTEM_PROMPT,
205
+ lines=8
206
+ )
207
+ retain_system_prompt_checkbox = gr.Checkbox(
208
+ label=i18n("新建对话保留Prompt"), value=False, visible=True, elem_classes="switch-checkbox")
209
+ with gr.Accordion(label=i18n("加载Prompt模板"), open=False):
210
  with gr.Column():
211
+ with gr.Row():
212
+ with gr.Column(scale=6):
213
+ templateFileSelectDropdown = gr.Dropdown(
214
+ label=i18n("选择Prompt模板集合文件"),
215
+ choices=get_template_names(),
216
+ multiselect=False,
217
+ value=get_template_names()[0],
218
+ container=False,
219
+ )
220
+ with gr.Column(scale=1):
221
+ templateRefreshBtn = gr.Button(
222
+ i18n("🔄 刷新"))
223
+ with gr.Row():
224
+ with gr.Column():
225
+ templateSelectDropdown = gr.Dropdown(
226
+ label=i18n("从Prompt模板中加载"),
227
+ choices=load_template(
228
+ get_template_names()[
229
+ 0], mode=1
230
+ ),
231
+ multiselect=False,
232
+ container=False,
233
+ )
234
+ gr.Markdown("---", elem_classes="hr-line")
235
+ with gr.Accordion(label=i18n("知识库"), open=True):
236
+ use_websearch_checkbox = gr.Checkbox(label=i18n(
237
+ "使用在线搜索"), value=False, elem_classes="switch-checkbox", elem_id="gr-websearch-cb", visible=False)
238
+ index_files = gr.Files(label=i18n(
239
+ "上传"), type="file", elem_id="upload-index-file")
240
+ two_column = gr.Checkbox(label=i18n(
241
+ "双栏pdf"), value=advance_docs["pdf"].get("two_column", False))
242
+ summarize_btn = gr.Button(i18n("总结"))
243
+ # TODO: 公式ocr
244
+ # formula_ocr = gr.Checkbox(label=i18n("识别公式"), value=advance_docs["pdf"].get("formula_ocr", False))
245
+
246
+ with gr.Tab(label=i18n("参数")):
247
+ gr.Markdown(i18n("# ⚠️ 务必谨慎更改 ⚠️"),
248
+ elem_id="advanced-warning")
249
+ with gr.Accordion(i18n("参数"), open=True):
250
+ temperature_slider = gr.Slider(
251
+ minimum=-0,
252
+ maximum=2.0,
253
+ value=1.0,
254
+ step=0.1,
255
+ interactive=True,
256
+ label="temperature",
257
+ )
258
+ top_p_slider = gr.Slider(
259
+ minimum=-0,
260
+ maximum=1.0,
261
+ value=1.0,
262
+ step=0.05,
263
+ interactive=True,
264
+ label="top-p",
265
+ )
266
+ n_choices_slider = gr.Slider(
267
+ minimum=1,
268
+ maximum=10,
269
+ value=1,
270
+ step=1,
271
+ interactive=True,
272
+ label="n choices",
273
+ )
274
+ stop_sequence_txt = gr.Textbox(
275
+ show_label=True,
276
+ placeholder=i18n("停止符,用英文逗号隔开..."),
277
+ label="stop",
278
+ value="",
279
+ lines=1,
280
+ )
281
+ max_context_length_slider = gr.Slider(
282
+ minimum=1,
283
+ maximum=32768,
284
+ value=2000,
285
+ step=1,
286
+ interactive=True,
287
+ label="max context",
288
+ )
289
+ max_generation_slider = gr.Slider(
290
+ minimum=1,
291
+ maximum=32768,
292
+ value=1000,
293
+ step=1,
294
+ interactive=True,
295
+ label="max generations",
296
+ )
297
+ presence_penalty_slider = gr.Slider(
298
+ minimum=-2.0,
299
+ maximum=2.0,
300
+ value=0.0,
301
+ step=0.01,
302
+ interactive=True,
303
+ label="presence penalty",
304
+ )
305
+ frequency_penalty_slider = gr.Slider(
306
+ minimum=-2.0,
307
+ maximum=2.0,
308
+ value=0.0,
309
+ step=0.01,
310
+ interactive=True,
311
+ label="frequency penalty",
312
+ )
313
+ logit_bias_txt = gr.Textbox(
314
+ show_label=True,
315
+ placeholder=f"word:likelihood",
316
+ label="logit bias",
317
+ value="",
318
+ lines=1,
319
+ )
320
+ user_identifier_txt = gr.Textbox(
321
+ show_label=True,
322
+ placeholder=i18n("用于定位滥用行为"),
323
+ label=i18n("用户名"),
324
+ value=user_name.value,
325
+ lines=1,
326
+ )
327
+ with gr.Tab(label=i18n("拓展")):
328
+ gr.Markdown(
329
+ "Will be here soon...\n(We hope)\n\nAnd we hope you can help us to make more extensions!")
330
+
331
+ # changeAPIURLBtn = gr.Button(i18n("🔄 切换API地址"))
332
+
333
+ with gr.Row(elem_id="popup-wrapper"):
334
+ with gr.Box(elem_id="chuanhu-popup"):
335
+ with gr.Box(elem_id="chuanhu-setting"):
336
+ with gr.Row():
337
+ gr.Markdown("## "+i18n("设置"))
338
+ gr.HTML(get_html("close_btn.html").format(
339
+ obj="box"), elem_classes="close-btn")
340
+ with gr.Tabs(elem_id="chuanhu-setting-tabs"):
341
+ with gr.Tab(label=i18n("模型")):
342
+ keyTxt = gr.Textbox(
343
  show_label=True,
344
+ placeholder=f"Your API-key...",
345
+ value=hide_middle_chars(user_api_key.value),
346
+ type="password",
347
+ visible=not HIDE_MY_KEY,
348
+ label="API-Key",
349
  )
350
+ if multi_api_key:
351
+ usageTxt = gr.Markdown(i18n(
352
+ "多账号模式已开启,无需输入key,可直接开始对话"), elem_id="usage-display", elem_classes="insert-block", visible=show_api_billing)
353
+ else:
354
+ usageTxt = gr.Markdown(i18n(
355
+ "**发送消息** 或 **提交key** 以显示额度"), elem_id="usage-display", elem_classes="insert-block", visible=show_api_billing)
356
+ # model_select_dropdown = gr.Dropdown(
357
+ # label=i18n("选择模型"), choices=MODELS, multiselect=False, value=MODELS[DEFAULT_MODEL], interactive=True
358
+ # )
359
+ # lora_select_dropdown = gr.Dropdown(
360
+ # label=i18n("选择LoRA模型"), choices=[], multiselect=False, interactive=True, visible=False
361
+ # )
362
+ # with gr.Row():
363
+
364
+ language_select_dropdown = gr.Dropdown(
365
+ label=i18n("选择回复语言(针对搜索&索引功能)"),
366
+ choices=REPLY_LANGUAGES,
367
+ multiselect=False,
368
+ value=REPLY_LANGUAGES[0],
369
  )
370
+
371
+ with gr.Tab(label=i18n("高级")):
372
+ gr.HTML(get_html("appearance_switcher.html").format(
373
+ label=i18n("切换亮暗色主题")), elem_classes="insert-block", visible=False)
374
+ use_streaming_checkbox = gr.Checkbox(
375
+ label=i18n("实时传输回答"), value=True, visible=ENABLE_STREAMING_OPTION, elem_classes="switch-checkbox"
 
 
 
 
 
 
 
 
 
376
  )
377
+ name_chat_method = gr.Dropdown(
378
+ label=i18n("对话命名方式"),
379
+ choices=HISTORY_NAME_METHODS,
380
+ multiselect=False,
 
381
  interactive=True,
382
+ value=HISTORY_NAME_METHODS[chat_name_method_index],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  )
384
+ single_turn_checkbox = gr.Checkbox(label=i18n(
385
+ "单轮对话"), value=False, elem_classes="switch-checkbox", elem_id="gr-single-session-cb", visible=False)
386
+ # checkUpdateBtn = gr.Button(i18n("🔄 检查更新..."), visible=check_update)
387
 
388
+ with gr.Tab(i18n("网络")):
389
+ gr.Markdown(
390
+ i18n("⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置"), elem_id="netsetting-warning")
391
  default_btn = gr.Button(i18n("🔙 恢复默认网络设置"))
392
  # 网络代理
393
  proxyTxt = gr.Textbox(
 
413
  # container=False,
414
  elem_classes="view-only-textbox no-container",
415
  )
 
 
416
 
417
+ with gr.Tab(label=i18n("关于"), elem_id="about-tab"):
418
+ gr.Markdown(
419
+ '<img alt="Chuanhu Chat logo" src="file=web_assets/icon/any-icon-512.png" style="max-width: 144px;">')
420
+ gr.Markdown("# "+i18n("川虎Chat"))
421
+ gr.HTML(get_html("footer.html").format(
422
+ versions=versions_html()), elem_id="footer")
423
+ gr.Markdown(CHUANHU_DESCRIPTION, elem_id="description")
424
+
425
+ with gr.Box(elem_id="chuanhu-training"):
426
+ with gr.Row():
427
+ gr.Markdown("## "+i18n("训练"))
428
+ gr.HTML(get_html("close_btn.html").format(
429
+ obj="box"), elem_classes="close-btn")
430
+ with gr.Tabs(elem_id="chuanhu-training-tabs"):
431
+ with gr.Tab(label="OpenAI "+i18n("微调")):
432
+ openai_train_status = gr.Markdown(label=i18n("训练状态"), value=i18n(
433
+ "查看[使用介绍](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35)"))
434
+
435
+ with gr.Tab(label=i18n("准备数据集")):
436
+ dataset_preview_json = gr.JSON(
437
+ label=i18n("数据集预览"))
438
+ dataset_selection = gr.Files(label=i18n("选择数据集"), file_types=[
439
+ ".xlsx", ".jsonl"], file_count="single")
440
+ upload_to_openai_btn = gr.Button(
441
+ i18n("上传到OpenAI"), variant="primary", interactive=False)
442
+
443
+ with gr.Tab(label=i18n("训练")):
444
+ openai_ft_file_id = gr.Textbox(label=i18n(
445
+ "文件ID"), value="", lines=1, placeholder=i18n("上传到 OpenAI 后自动填充"))
446
+ openai_ft_suffix = gr.Textbox(label=i18n(
447
+ "模型名称后缀"), value="", lines=1, placeholder=i18n("可选,用于区分不同的模型"))
448
+ openai_train_epoch_slider = gr.Slider(label=i18n(
449
+ "训练轮数(Epochs)"), minimum=1, maximum=100, value=3, step=1, interactive=True)
450
+ openai_start_train_btn = gr.Button(
451
+ i18n("开始训练"), variant="primary", interactive=False)
452
+
453
+ with gr.Tab(label=i18n("状态")):
454
+ openai_status_refresh_btn = gr.Button(i18n("刷新状态"))
455
+ openai_cancel_all_jobs_btn = gr.Button(
456
+ i18n("取消所有任务"))
457
+ add_to_models_btn = gr.Button(
458
+ i18n("添加训练好的模型到模型列表"), interactive=False)
459
+
460
+ with gr.Box(elem_id="web-config", visible=False):
461
+ gr.HTML(get_html('web_config.html').format(
462
+ enableCheckUpdate_config=check_update,
463
+ hideHistoryWhenNotLoggedIn_config=hide_history_when_not_logged_in,
464
+ forView_i18n=i18n("仅供查看"),
465
+ deleteConfirm_i18n_pref=i18n("你真的要删除 "),
466
+ deleteConfirm_i18n_suff=i18n(" 吗?"),
467
+ usingLatest_i18n=i18n("您使用的就是最新版!"),
468
+ updatingMsg_i18n=i18n("正在尝试更新..."),
469
+ updateSuccess_i18n=i18n("更新成功,请重启本程序"),
470
+ updateFailure_i18n=i18n(
471
+ "更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)"),
472
+ regenerate_i18n=i18n("重新生成"),
473
+ deleteRound_i18n=i18n("删除这轮问答"),
474
+ renameChat_i18n=i18n("重命名该对话"),
475
+ validFileName_i18n=i18n("请输入有效的文件名,不要包含以下特殊字符:"),
476
+ ))
477
+ with gr.Box(elem_id="fake-gradio-components", visible=False):
478
+ updateChuanhuBtn = gr.Button(
479
+ visible=False, elem_classes="invisible-btn", elem_id="update-chuanhu-btn")
480
+ changeSingleSessionBtn = gr.Button(
481
+ visible=False, elem_classes="invisible-btn", elem_id="change-single-session-btn")
482
+ changeOnlineSearchBtn = gr.Button(
483
+ visible=False, elem_classes="invisible-btn", elem_id="change-online-search-btn")
484
+ historySelectBtn = gr.Button(
485
+ visible=False, elem_classes="invisible-btn", elem_id="history-select-btn") # Not used
486
 
487
  # https://github.com/gradio-app/gradio/pull/3296
488
+
489
  def create_greeting(request: gr.Request):
490
+ if hasattr(request, "username") and request.username: # is not None or is not ""
491
  logging.info(f"Get User Name: {request.username}")
492
+ user_info, user_name = gr.Markdown.update(
493
+ value=f"User: {request.username}"), request.username
494
  else:
495
+ user_info, user_name = gr.Markdown.update(
496
+ value=f"", visible=False), ""
497
+ current_model = get_model(
498
+ model_name=MODELS[DEFAULT_MODEL], access_key=my_api_key)[0]
499
  current_model.set_user_identifier(user_name)
500
+ if not hide_history_when_not_logged_in or user_name:
501
+ filename, system_prompt, chatbot = current_model.auto_load()
502
+ else:
503
+ system_prompt = gr.update()
504
+ filename = gr.update()
505
+ chatbot = gr.Chatbot.update(label=MODELS[DEFAULT_MODEL])
506
+ return user_info, user_name, current_model, toggle_like_btn_visibility(DEFAULT_MODEL), filename, system_prompt, chatbot, init_history_list(user_name)
507
+ demo.load(create_greeting, inputs=None, outputs=[
508
+ user_info, user_name, current_model, like_dislike_area, saveFileName, systemPromptTxt, chatbot, historySelectList], api_name="load")
509
  chatgpt_predict_args = dict(
510
  fn=predict,
511
  inputs=[
 
537
  )
538
 
539
  transfer_input_args = dict(
540
+ fn=transfer_input, inputs=[user_input], outputs=[
541
+ user_question, user_input, submitBtn, cancelBtn], show_progress=True
542
  )
543
 
544
  get_usage_args = dict(
545
+ fn=billing_info, inputs=[current_model], outputs=[
546
+ usageTxt], show_progress=False
547
  )
548
 
549
  load_history_from_file_args = dict(
550
  fn=load_chat_history,
551
+ inputs=[current_model, historySelectList, user_name],
552
  outputs=[saveFileName, systemPromptTxt, chatbot]
553
  )
554
 
555
  refresh_history_args = dict(
556
+ fn=get_history_list, inputs=[user_name], outputs=[historySelectList]
557
  )
558
 
559
+ auto_name_chat_history_args = dict(
560
+ fn=auto_name_chat_history,
561
+ inputs=[current_model, name_chat_method, user_question, chatbot, user_name, single_turn_checkbox],
562
+ outputs=[historySelectList],
563
+ show_progress=False,
564
+ )
565
 
566
  # Chatbot
567
  cancelBtn.click(interrupt, [current_model], [])
568
 
569
+ user_input.submit(**transfer_input_args).then(**
570
+ chatgpt_predict_args).then(**end_outputing_args).then(**auto_name_chat_history_args)
571
  user_input.submit(**get_usage_args)
572
 
573
+ # user_input.submit(auto_name_chat_history, [current_model, user_question, chatbot, user_name], [historySelectList], show_progress=False)
574
+
575
+ submitBtn.click(**transfer_input_args).then(**chatgpt_predict_args,
576
+ api_name="predict").then(**end_outputing_args).then(**auto_name_chat_history_args)
577
  submitBtn.click(**get_usage_args)
578
 
579
+ # submitBtn.click(auto_name_chat_history, [current_model, user_question, chatbot, user_name], [historySelectList], show_progress=False)
580
+
581
+ index_files.upload(handle_file_upload, [current_model, index_files, chatbot, language_select_dropdown], [
582
+ index_files, chatbot, status_display])
583
+ summarize_btn.click(handle_summarize_index, [
584
+ current_model, index_files, chatbot, language_select_dropdown], [chatbot, status_display])
585
 
586
  emptyBtn.click(
587
  reset,
588
+ inputs=[current_model, retain_system_prompt_checkbox],
589
+ outputs=[chatbot, status_display, historySelectList, systemPromptTxt],
590
  show_progress=True,
591
+ _js='(a,b)=>{return clearChatbot(a,b);}',
592
  )
593
 
594
  retryBtn.click(**start_outputing_args).then(
 
636
  two_column.change(update_doc_config, [two_column], None)
637
 
638
  # LLM Models
639
+ keyTxt.change(set_key, [current_model, keyTxt], [
640
+ user_api_key, status_display], api_name="set_key").then(**get_usage_args)
641
  keyTxt.submit(**get_usage_args)
642
+ single_turn_checkbox.change(
643
+ set_single_turn, [current_model, single_turn_checkbox], None)
644
+ 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], [
645
+ current_model, status_display, chatbot, lora_select_dropdown, user_api_key, keyTxt], show_progress=True, api_name="get_model")
646
+ model_select_dropdown.change(toggle_like_btn_visibility, [model_select_dropdown], [
647
+ like_dislike_area], show_progress=False)
648
+ lora_select_dropdown.change(get_model, [model_select_dropdown, lora_select_dropdown, user_api_key, temperature_slider,
649
+ top_p_slider, systemPromptTxt, user_name, current_model], [current_model, status_display, chatbot], show_progress=True)
650
 
651
  # Template
652
+ systemPromptTxt.change(set_system_prompt, [
653
+ current_model, systemPromptTxt], None)
654
+ templateRefreshBtn.click(get_template_dropdown, None, [
655
+ templateFileSelectDropdown])
656
+ templateFileSelectDropdown.input(
657
  load_template,
658
  [templateFileSelectDropdown],
659
  [promptTemplates, templateSelectDropdown],
 
667
  )
668
 
669
  # S&L
670
+ renameHistoryBtn.click(
671
+ rename_chat_history,
672
  [current_model, saveFileName, chatbot, user_name],
673
+ [historySelectList],
674
  show_progress=True,
675
+ _js='(a,b,c,d)=>{return saveChatHistory(a,b,c,d);}'
676
  )
 
677
  exportMarkdownBtn.click(
678
  export_markdown,
679
  [current_model, saveFileName, chatbot, user_name],
680
+ [],
681
  show_progress=True,
682
  )
683
  historyRefreshBtn.click(**refresh_history_args)
684
+ historyDeleteBtn.click(delete_chat_history, [current_model, historySelectList, user_name], [status_display, historySelectList, chatbot], _js='(a,b,c)=>{return showConfirmationDialog(a, b, c);}').then(
685
+ reset,
686
+ inputs=[current_model, retain_system_prompt_checkbox],
687
+ outputs=[chatbot, status_display, historySelectList, systemPromptTxt],
688
+ show_progress=True,
689
+ _js='(a,b)=>{return clearChatbot(a,b);}',
690
+ )
691
+ historySelectList.input(**load_history_from_file_args)
692
+ uploadFileBtn.upload(upload_chat_history, [current_model, uploadFileBtn, user_name], [
693
+ saveFileName, systemPromptTxt, chatbot]).then(**refresh_history_args)
694
+ historyDownloadBtn.click(None, [
695
+ user_name, historySelectList], None, _js='(a,b)=>{return downloadHistory(a,b,".json");}')
696
+ historyMarkdownDownloadBtn.click(None, [
697
+ user_name, historySelectList], None, _js='(a,b)=>{return downloadHistory(a,b,".md");}')
698
+ historySearchTextbox.input(
699
+ filter_history,
700
+ [user_name, historySearchTextbox],
701
+ [historySelectList]
702
+ )
703
 
704
  # Train
705
+ dataset_selection.upload(handle_dataset_selection, dataset_selection, [
706
+ dataset_preview_json, upload_to_openai_btn, openai_train_status])
707
+ dataset_selection.clear(handle_dataset_clear, [], [
708
+ dataset_preview_json, upload_to_openai_btn])
709
+ upload_to_openai_btn.click(upload_to_openai, [dataset_selection], [
710
+ openai_ft_file_id, openai_train_status], show_progress=True)
711
+
712
+ openai_ft_file_id.change(lambda x: gr.update(interactive=True) if len(
713
+ x) > 0 else gr.update(interactive=False), [openai_ft_file_id], [openai_start_train_btn])
714
+ openai_start_train_btn.click(start_training, [
715
+ openai_ft_file_id, openai_ft_suffix, openai_train_epoch_slider], [openai_train_status])
716
+
717
+ openai_status_refresh_btn.click(get_training_status, [], [
718
+ openai_train_status, add_to_models_btn])
719
+ add_to_models_btn.click(add_to_models, [], [
720
+ model_select_dropdown, openai_train_status], show_progress=True)
721
+ openai_cancel_all_jobs_btn.click(
722
+ cancel_all_jobs, [], [openai_train_status], show_progress=True)
723
 
724
  # Advanced
725
+ max_context_length_slider.change(
726
+ set_token_upper_limit, [current_model, max_context_length_slider], None)
727
+ temperature_slider.change(
728
+ set_temperature, [current_model, temperature_slider], None)
729
  top_p_slider.change(set_top_p, [current_model, top_p_slider], None)
730
+ n_choices_slider.change(
731
+ set_n_choices, [current_model, n_choices_slider], None)
732
+ stop_sequence_txt.change(
733
+ set_stop_sequence, [current_model, stop_sequence_txt], None)
734
+ max_generation_slider.change(
735
+ set_max_tokens, [current_model, max_generation_slider], None)
736
+ presence_penalty_slider.change(
737
+ set_presence_penalty, [current_model, presence_penalty_slider], None)
738
+ frequency_penalty_slider.change(
739
+ set_frequency_penalty, [current_model, frequency_penalty_slider], None)
740
+ logit_bias_txt.change(
741
+ set_logit_bias, [current_model, logit_bias_txt], None)
742
+ user_identifier_txt.change(set_user_identifier, [
743
+ current_model, user_identifier_txt], None)
744
 
745
  default_btn.click(
746
  reset_default, [], [apihostTxt, proxyTxt, status_display], show_progress=True
 
757
  # [status_display],
758
  # show_progress=True,
759
  # )
760
+ # checkUpdateBtn.click(fn=None, _js='manualCheckUpdate')
761
 
762
  # Invisible elements
763
  updateChuanhuBtn.click(
 
766
  [status_display],
767
  show_progress=True,
768
  )
769
+ changeSingleSessionBtn.click(
770
+ fn=lambda value: gr.Checkbox.update(value=value),
771
+ inputs=[single_turn_checkbox],
772
+ outputs=[single_turn_checkbox],
773
+ _js='(a)=>{return bgChangeSingleSession(a);}'
774
+ )
775
+ changeOnlineSearchBtn.click(
776
+ fn=lambda value: gr.Checkbox.update(value=value),
777
+ inputs=[use_websearch_checkbox],
778
+ outputs=[use_websearch_checkbox],
779
+ _js='(a)=>{return bgChangeOnlineSearch(a);}'
780
+ )
781
+ historySelectBtn.click( # This is an experimental feature... Not actually used.
782
+ fn=load_chat_history,
783
+ inputs=[current_model, historySelectList],
784
+ outputs=[saveFileName, systemPromptTxt, chatbot],
785
+ _js='(a,b)=>{return bgSelectHistory(a,b);}'
786
+ )
787
+
788
 
789
  logging.info(
790
  colorama.Back.GREEN
 
797
  if __name__ == "__main__":
798
  reload_javascript()
799
  demo.queue(concurrency_count=CONCURRENT_COUNT).launch(
800
+ allowed_paths=["history", "web_assets"],
801
+ server_name=server_name,
802
+ server_port=server_port,
803
+ share=share,
804
+ auth=auth_from_conf if authflag else None,
805
  favicon_path="./web_assets/favicon.ico",
806
+ inbrowser=not dockerflag, # 禁止在docker下开启inbrowser
807
  )
config_example.json CHANGED
@@ -11,6 +11,10 @@
11
  "midjourney_proxy_api_secret": "", // 你的 MidJourney Proxy API Secret,用于鉴权访问 api,可选
12
  "midjourney_discord_proxy_url": "", // 你的 MidJourney Discord Proxy URL,用于对生成对图进行反代,可选
13
  "midjourney_temp_folder": "./tmp", // 你的 MidJourney 临时文件夹,用于存放生成的图片,填空则关闭自动下载切图(直接显示MJ的四宫格图)
 
 
 
 
14
 
15
 
16
  //== Azure ==
@@ -23,14 +27,15 @@
23
  "azure_embedding_model_name": "text-embedding-ada-002", // 你的 Azure OpenAI Embedding 模型名称
24
 
25
  //== 基础配置 ==
26
- "language": "auto", // 界面语言,可选"auto", "zh-CN", "en-US", "ja-JP", "ko-KR", "sv-SE"
27
  "users": [], // 用户列表,[[用户名1, 密码1], [用户名2, 密码2], ...]
28
  "local_embedding": false, //是否在本地编制索引
29
  "hide_history_when_not_logged_in": false, //未登录情况下是否不展示对话历史
30
  "check_update": true, //是否启用检查更新
31
  "default_model": "gpt-3.5-turbo", // 默认模型
32
- "bot_avatar": "default", // 机器人头像,可填写图片链接、Data URL (base64),或者"none"(不显示头像)
33
- "user_avatar": "default", // 用户头像,可填写图片链接、Data URL (base64),或者"none"(不显示头像)
 
34
 
35
  //== API 用量 ==
36
  "show_api_billing": false, //是否显示OpenAI API用量(启用需要填写sensitive_id)
@@ -57,11 +62,13 @@
57
  //== 高级配置 ==
58
  // 是否多个API Key轮换使用
59
  "multi_api_key": false,
60
- "api_key_list": [
61
- "sk-xxxxxxxxxxxxxxxxxxxxxxxx1",
62
- "sk-xxxxxxxxxxxxxxxxxxxxxxxx2",
63
- "sk-xxxxxxxxxxxxxxxxxxxxxxxx3"
64
- ],
 
 
65
  // 自定义OpenAI API Base
66
  // "openai_api_base": "https://api.openai.com",
67
  // 自定义使用代理(请替换代理URL)
 
11
  "midjourney_proxy_api_secret": "", // 你的 MidJourney Proxy API Secret,用于鉴权访问 api,可选
12
  "midjourney_discord_proxy_url": "", // 你的 MidJourney Discord Proxy URL,用于对生成对图进行反代,可选
13
  "midjourney_temp_folder": "./tmp", // 你的 MidJourney 临时文件夹,用于存放生成的图片,填空则关闭自动下载切图(直接显示MJ的四宫格图)
14
+ "spark_appid": "", // 你的 讯飞星火大模型 API AppID,用于讯飞星火大模型对话模型
15
+ "spark_api_key": "", // 你的 讯飞星火大模型 API Key,用于讯飞星火大模型对话模型
16
+ "spark_api_secret": "", // 你的 讯飞星火大模型 API Secret,用于讯飞星火大模型对话模型
17
+ "claude_api_secret":"",// 你的 Claude API Secret,用于 Claude 对话模型
18
 
19
 
20
  //== Azure ==
 
27
  "azure_embedding_model_name": "text-embedding-ada-002", // 你的 Azure OpenAI Embedding 模型名称
28
 
29
  //== 基础配置 ==
30
+ "language": "auto", // 界面语言,可选"auto", "zh_CN", "en_US", "ja_JP", "ko_KR", "sv_SE", "ru_RU", "vi_VN"
31
  "users": [], // 用户列表,[[用户名1, 密码1], [用户名2, 密码2], ...]
32
  "local_embedding": false, //是否在本地编制索引
33
  "hide_history_when_not_logged_in": false, //未登录情况下是否不展示对话历史
34
  "check_update": true, //是否启用检查更新
35
  "default_model": "gpt-3.5-turbo", // 默认模型
36
+ "chat_name_method_index": 2, // 选择对话名称的方法。0: 使用日期时间命名;1: 使用第一条提问命名,2: 使用模型自动总结
37
+ "bot_avatar": "default", // 机器人头像,可填写本地或网络图片链接,或者"none"(不显示头像)
38
+ "user_avatar": "default", // 用户头像,可填写本地或网络图片链接,或者"none"(不显示头像)
39
 
40
  //== API 用量 ==
41
  "show_api_billing": false, //是否显示OpenAI API用量(启用需要填写sensitive_id)
 
62
  //== 高级配置 ==
63
  // 是否多个API Key轮换使用
64
  "multi_api_key": false,
65
+ // "available_models": ["GPT3.5 Turbo", "GPT4 Turbo", "GPT4 Vision"], // 可用的模型列表,将覆盖默认的可用模型列表
66
+ // "extra_models": ["模型名称3", "模型名称4", ...], // 额外的模型,将添加到可用的模型列表之后
67
+ // "api_key_list": [
68
+ // "sk-xxxxxxxxxxxxxxxxxxxxxxxx1",
69
+ // "sk-xxxxxxxxxxxxxxxxxxxxxxxx2",
70
+ // "sk-xxxxxxxxxxxxxxxxxxxxxxxx3"
71
+ // ],
72
  // 自定义OpenAI API Base
73
  // "openai_api_base": "https://api.openai.com",
74
  // 自定义使用代理(请替换代理URL)
locale/en_US.json CHANGED
@@ -1,87 +1,141 @@
1
  {
2
- "未命名对话历史记录": "Unnamed Dialog History",
3
- "在这里输入": "Type in here",
4
- "🧹 新的对话": "🧹 New Dialogue",
5
- "🔄 重新生成": "🔄 Regeneration",
6
- "🗑️ 删除最旧对话": "🗑️ Delete oldest dialog",
7
- "🗑️ 删除最新对话": "🗑️ Delete latest dialog",
8
- "模型": "Model",
9
- "多账号模式已开启,无需输入key,可直接开始对话": "Multi-account mode is enabled, no need to enter key, you can start the dialogue directly",
10
  "**发送消息** 或 **提交key** 以显示额度": "**Send message** or **Submit key** to display credit",
11
- "选择模型": "Select Model",
12
- "选择LoRA模型": "Select LoRA Model",
13
- "实时传输回答": "Stream output",
14
- "单轮对话": "Single-turn dialogue",
15
- "使用在线搜索": "Use online search",
16
- "选择回复语言(针对搜索&索引功能)": "Select reply language (for search & index)",
17
- "上传索引文件": "Upload",
18
- "双栏pdf": "Two-column pdf",
19
- "识别公式": "formula OCR",
20
- "在这里输入System Prompt...": "Type in System Prompt here...",
21
- "加载Prompt模板": "Load Prompt Template",
22
- "选择Prompt模板集合文件": "Select Prompt Template Collection File",
23
- "🔄 刷新": "🔄 Refresh",
 
 
 
 
 
24
  "从Prompt模板中加载": "Load from Prompt Template",
25
- "保存/加载": "Save/Load",
26
- "保存/加载对话历史记录": "Save/Load Dialog History",
27
  "从列表中加载对话": "Load dialog from list",
28
- "设置文件名: 默认为.json,可选为.md": "Set file name: default is .json, optional is .md",
29
- "设置保存文件名": "Set save file name",
30
- "对话历史记录": "Dialog History",
31
- "💾 保存对话": "💾 Save Dialog",
32
- "📝 导出为Markdown": "📝 Export as Markdown",
33
- "默认保存于history文件夹": "Default save in history folder",
34
- "高级": "Advanced",
35
- "# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ Caution: Changes require care. ⚠️",
36
- "参数": "Parameters",
37
- "停止符,用英文逗号隔开...": "Type in stop token here, separated by comma...",
38
- "用于定位滥用行为": "Used to locate abuse",
39
- "用户名": "Username",
40
- "在这里输入API-Host...": "Type in API-Host here...",
41
- "🔄 切换API地址": "🔄 Switch API Address",
42
- "未设置代理...": "No proxy...",
43
  "代理地址": "Proxy address",
44
- "🔄 设置代理地址": "🔄 Set Proxy Address",
45
- "🔙 恢复默认网络设置": "🔙 Reset Network Settings",
46
- "🔄 检查更新...": "🔄 Check for Update...",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  "取消": "Cancel",
48
- "更新": "Update",
49
- "详情": "Details",
 
 
 
 
 
 
50
  "好": "OK",
51
- "更新成功,请重启本程序": "Updated successfully, please restart this program",
52
- "更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "Update failed, please try [manually updating](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)",
 
 
 
 
 
53
  "川虎Chat 🚀": "Chuanhu Chat 🚀",
 
 
54
  "开始实时传输回答……": "Start streaming output...",
55
- "Token 计数: ": "Token Count: ",
56
- ",本次对话累计消耗了 ": ", Total cost for this dialogue is ",
57
- "**获取API使用情况失败**": "**Failed to get API usage**",
58
- "**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**Failed to get API usage**, correct sensitive_id needed in `config.json`",
59
- "**获取API使用情况失败**,sensitive_id错误或已过期": "**Failed to get API usage**, wrong or expired sensitive_id",
60
- "**本月使用金额** ": "**Monthly usage** ",
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  "本月使用金额": "Monthly usage",
62
- "获取API使用情况失败:": "Failed to get API usage:",
63
- "API密钥更改为了": "The API key is changed to",
64
- "JSON解析错误,收到的内容: ": "JSON parsing error, received content: ",
 
 
65
  "模型设置为了:": "Model is set to: ",
66
- "☹️发生了错误:": "☹️Error: ",
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  "获取对话时发生错误,请查看后台日志": "Error occurred when getting dialogue, check the background log",
 
 
 
 
 
 
 
 
 
68
  "请检查网络连接,或者API-Key是否有效。": "Check the network connection or whether the API-Key is valid.",
69
- "连接超时,无法获取对话。": "Connection timed out, unable to get dialogue.",
70
- "读取超时,无法获取对话。": "Read timed out, unable to get dialogue.",
71
- "代理错误,无法获取对话。": "Proxy error, unable to get dialogue.",
72
- "SSL错误,无法获取对话。": "SSL error, unable to get dialogue.",
73
- "API key为空,请检查是否输入正确。": "API key is empty, check whether it is entered correctly.",
74
  "请输入对话内容。": "Enter the content of the conversation.",
 
 
75
  "账单信息不适用": "Billing information is not applicable",
76
- "由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "Developed by Bilibili [土川虎虎虎](https://space.bilibili.com/29125536), [明昭MZhao](https://space.bilibili.com/24807452) and [Keldos](https://github.com/Keldos-Li)\n\nDownload latest code from [GitHub](https://github.com/GaiZhenbiao/ChuanhuChatGPT)",
77
- "切换亮暗色主题": "Switch light/dark theme",
78
- "您的IP区域:未知。": "Your IP region: Unknown.",
79
- "获取IP地理位置失败。原因:": "Failed to get IP location. Reason: ",
80
- "。你仍然可以使用聊天功能。": ". You can still use the chat function.",
81
- "您的IP区域:": "Your IP region: ",
82
- "总结": "Summarize",
83
- "生成内容总结中……": "Generating content summary...",
84
- "由于下面的原因,Google 拒绝返回 PaLM 的回答:\n\n": "Due to the following reasons, Google refuses to provide an answer to PaLM: \n\n",
85
- "---\n⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "---\n⚠️ To ensure the security of API-Key, please modify the network settings in the configuration file `config.json`.",
86
- "网络参数": "Network parameter"
87
- }
 
 
 
 
 
 
 
 
 
 
 
1
  {
2
+ " 吗?": " ?",
3
+ "# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ Caution: Changes require care. ⚠️",
 
 
 
 
 
 
4
  "**发送消息** 或 **提交key** 以显示额度": "**Send message** or **Submit key** to display credit",
5
+ "**本月使用金额** ": "**Monthly usage** ",
6
+ "**获取API使用情况失败**": "**Failed to get API usage**",
7
+ "**获取API使用情况失败**,sensitive_id错误或已过期": "**Failed to get API usage**, wrong or expired sensitive_id",
8
+ "**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**Failed to get API usage**, correct sensitive_id needed in `config.json`",
9
+ "API key为空,请检查是否输入正确。": "API key is empty, check whether it is entered correctly.",
10
+ "API密钥更改为了": "The API key is changed to",
11
+ "JSON解析错误,收到的内容: ": "JSON parsing error, received content: ",
12
+ "SSL错误,无法获取对话。": "SSL error, unable to get dialogue.",
13
+ "Token 计数: ": "Token Count: ",
14
+ "☹️发生了错误:": "☹️Error: ",
15
+ "⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "⚠️ To ensure the security of API-Key, please modify the network settings in the configuration file `config.json`.",
16
+ "。你仍然可以使用聊天功能。": ". You can still use the chat function.",
17
+ "上传": "Upload",
18
+ "上传了": "Uploaded",
19
+ "上传到 OpenAI 后自动填充": "Automatically filled after uploading to OpenAI",
20
+ "上传到OpenAI": "Upload to OpenAI",
21
+ "上传文件": "Upload files",
22
+ "仅供查看": "For viewing only",
23
  "从Prompt模板中加载": "Load from Prompt Template",
 
 
24
  "从列表中加载对话": "Load dialog from list",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  "代理地址": "Proxy address",
26
+ "代理错误,无法获取对话。": "Proxy error, unable to get dialogue.",
27
+ "你没有权限访问 GPT4,[进一步了解](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)": "You do not have permission to access GPT-4, [learn more](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)",
28
+ "你没有选择任何对话历史": "You have not selected any conversation history.",
29
+ "你真的要删除 ": "Are you sure you want to delete ",
30
+ "使用在线搜索": "Use online search",
31
+ "停止符,用英文逗号隔开...": "Type in stop token here, separated by comma...",
32
+ "关于": "About",
33
+ "准备数据集": "Prepare Dataset",
34
+ "切换亮暗色主题": "Switch light/dark theme",
35
+ "删除对话历史成功": "Successfully deleted conversation history.",
36
+ "删除这轮问答": "Delete this round of Q&A",
37
+ "刷新状态": "Refresh Status",
38
+ "��余配额不足,[进一步了解](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)": "Insufficient remaining quota, [learn more](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)",
39
+ "加载Prompt模板": "Load Prompt Template",
40
+ "单轮对话": "Single-turn",
41
+ "历史记录(JSON)": "History file (JSON)",
42
+ "参数": "Parameters",
43
+ "双栏pdf": "Two-column pdf",
44
  "取消": "Cancel",
45
+ "取消所有任务": "Cancel All Tasks",
46
+ "可选,用于区分不同的模型": "Optional, used to distinguish different models",
47
+ "启用的工具:": "Enabled tools: ",
48
+ "在工具箱中管理知识库文件": "Manage knowledge base files in the toolbox",
49
+ "在线搜索": "Web search",
50
+ "在这里输入": "Type in here",
51
+ "在这里输入System Prompt...": "Type in System Prompt here...",
52
+ "多账号模式已开启,无需输入key,可直接开始对话": "Multi-account mode is enabled, no need to enter key, you can start the dialogue directly",
53
  "好": "OK",
54
+ "实时传输回答": "Stream output",
55
+ "对话": "Dialogue",
56
+ "对话历史": "Conversation history",
57
+ "对话历史记录": "Dialog History",
58
+ "对话命名方式": "History naming method",
59
+ "导出为 Markdown": "Export as Markdown",
60
+ "川虎Chat": "Chuanhu Chat",
61
  "川虎Chat 🚀": "Chuanhu Chat 🚀",
62
+ "工具箱": "Toolbox",
63
+ "已经被删除啦": "It has been deleted.",
64
  "开始实时传输回答……": "Start streaming output...",
65
+ "开始训练": "Start Training",
66
+ "微调": "Fine-tuning",
67
+ "总结": "Summarize",
68
+ "总结完成": "Summary completed.",
69
+ "您使用的就是最新版!": "You are using the latest version!",
70
+ "您的IP区域:": "Your IP region: ",
71
+ "您的IP区域:未知。": "Your IP region: Unknown.",
72
+ "拓展": "Extensions",
73
+ "搜索(支持正则)...": "Search (supports regex)...",
74
+ "数据集预览": "Dataset Preview",
75
+ "文件ID": "File ID",
76
+ "新对话 ": "New Chat ",
77
+ "新建对话保留Prompt": "Retain Prompt For New Chat",
78
+ "暂时未知": "Unknown",
79
+ "更新": "Update",
80
+ "更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "Update failed, please try [manually updating](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)",
81
+ "更新成功,请重启本程序": "Updated successfully, please restart this program",
82
+ "未命名对话历史记录": "Unnamed Dialog History",
83
+ "未设置代理...": "No proxy...",
84
  "本月使用金额": "Monthly usage",
85
+ "查看[使用介绍](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35)": "View the [usage guide](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35) for more details",
86
+ "根据日期时间": "By date and time",
87
+ "模型": "Model",
88
+ "模型名称后缀": "Model Name Suffix",
89
+ "模型自动总结(消耗tokens)": "Auto summary by LLM (Consume tokens)",
90
  "模型设置为了:": "Model is set to: ",
91
+ "正在尝试更新...": "Trying to update...",
92
+ "添加训练好的模型到模型列表": "Add trained model to the model list",
93
+ "状态": "Status",
94
+ "生成内容总结中……": "Generating content summary...",
95
+ "用于定位滥用行为": "Used to locate abuse",
96
+ "用户名": "Username",
97
+ "由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "Developed by Bilibili [土川虎虎虎](https://space.bilibili.com/29125536), [明昭MZhao](https://space.bilibili.com/24807452) and [Keldos](https://github.com/Keldos-Li)\n\nDownload latest code from [GitHub](https://github.com/GaiZhenbiao/ChuanhuChatGPT)",
98
+ "知识库": "Knowledge base",
99
+ "知识库文件": "Knowledge base files",
100
+ "第一条提问": "By first question",
101
+ "索引构建完成": "Indexing complete.",
102
+ "网络": "Network",
103
+ "获取API使用情况失败:": "Failed to get API usage:",
104
+ "获取IP地理位置失败。原因:": "Failed to get IP location. Reason: ",
105
  "获取对话时发生错误,请查看后台日志": "Error occurred when getting dialogue, check the background log",
106
+ "训练": "Training",
107
+ "训练状态": "Training Status",
108
+ "训练轮数(Epochs)": "Training Epochs",
109
+ "设置": "Settings",
110
+ "设置保存文件名": "Set save file name",
111
+ "设置文件名: 默认为.json,可选为.md": "Set file name: default is .json, optional is .md",
112
+ "识别公式": "formula OCR",
113
+ "详情": "Details",
114
+ "请查看 config_example.json,配置 Azure OpenAI": "Please review config_example.json to configure Azure OpenAI",
115
  "请检查网络连接,或者API-Key是否有效。": "Check the network connection or whether the API-Key is valid.",
 
 
 
 
 
116
  "请输入对话内容。": "Enter the content of the conversation.",
117
+ "请输入有效的文件名,不要包含以下特殊字符:": "Please enter a valid file name, do not include the following special characters: ",
118
+ "读取超时,无法获取对话。": "Read timed out, unable to get dialogue.",
119
  "账单信息不适用": "Billing information is not applicable",
120
+ "连接超时,无法获取对话。": "Connection timed out, unable to get dialogue.",
121
+ "选择LoRA模型": "Select LoRA Model",
122
+ "选择Prompt模板集合文件": "Select Prompt Template Collection File",
123
+ "选择回复语言(针对搜索&索引功能)": "Select reply language (for search & index)",
124
+ "选择数据集": "Select Dataset",
125
+ "选择模型": "Select Model",
126
+ "重命名该对话": "Rename this chat",
127
+ "重新生成": "Regenerate",
128
+ "高级": "Advanced",
129
+ ",本次对话累计消耗了 ": ", total cost: ",
130
+ "💾 保存对话": "💾 Save Dialog",
131
+ "📝 导出为 Markdown": "📝 Export as Markdown",
132
+ "🔄 切换API地址": "🔄 Switch API Address",
133
+ "🔄 刷新": "🔄 Refresh",
134
+ "🔄 检查更新...": "🔄 Check for Update...",
135
+ "🔄 设置代理地址": "🔄 Set Proxy Address",
136
+ "🔄 重新生成": "🔄 Regeneration",
137
+ "🔙 恢复默认网络设置": "🔙 Reset Network Settings",
138
+ "🗑️ 删除最新对话": "🗑️ Delete latest dialog",
139
+ "🗑️ 删除最旧对话": "🗑️ Delete oldest dialog",
140
+ "🧹 新的对话": "🧹 New Dialogue"
141
+ }
locale/extract_locale.py CHANGED
@@ -1,26 +1,138 @@
1
- import os
2
- import json
3
- import re
4
 
5
- # Define regular expression patterns
6
- pattern = r'i18n\((\"{3}.*?\"{3}|\".*?\")\)'
7
 
8
- # Load the .py file
9
- with open('ChuanhuChatbot.py', 'r', encoding='utf-8') as f:
10
- contents = f.read()
 
11
 
12
- # Load the .py files in the modules folder
13
- for filename in os.listdir("modules"):
14
- if filename.endswith(".py"):
15
- with open(os.path.join("modules", filename), "r", encoding="utf-8") as f:
16
- contents += f.read()
17
 
18
- # Matching with regular expressions
19
- matches = re.findall(pattern, contents, re.DOTALL)
20
 
21
- # Convert to key/value pairs
22
- data = {match.strip('()"'): '' for match in matches}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- # Save as a JSON file
25
- with open('labels.json', 'w', encoding='utf-8') as f:
26
- json.dump(data, f, ensure_ascii=False, indent=4)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, json, re, sys
2
+ import aiohttp, asyncio
3
+ import commentjson
4
 
5
+ asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())
 
6
 
7
+ with open("config.json", "r", encoding="utf-8") as f:
8
+ config = commentjson.load(f)
9
+ api_key = config["openai_api_key"]
10
+ url = config["openai_api_base"] + "/v1/chat/completions" if "openai_api_base" in config else "https://api.openai.com/v1/chat/completions"
11
 
 
 
 
 
 
12
 
13
+ def get_current_strings():
14
+ pattern = r'i18n\s*\(\s*["\']([^"\']*(?:\)[^"\']*)?)["\']\s*\)'
15
 
16
+ # Load the .py files
17
+ contents = ""
18
+ for dirpath, dirnames, filenames in os.walk("."):
19
+ for filename in filenames:
20
+ if filename.endswith(".py"):
21
+ filepath = os.path.join(dirpath, filename)
22
+ with open(filepath, 'r', encoding='utf-8') as f:
23
+ contents += f.read()
24
+ # Matching with regular expressions
25
+ matches = re.findall(pattern, contents, re.DOTALL)
26
+ data = {match.strip('()"'): '' for match in matches}
27
+ fixed_data = {} # fix some keys
28
+ for key, value in data.items():
29
+ if "](" in key and key.count("(") != key.count(")"):
30
+ fixed_data[key+")"] = value
31
+ else:
32
+ fixed_data[key] = value
33
 
34
+ return fixed_data
35
+
36
+
37
+ def get_locale_strings(filename):
38
+ try:
39
+ with open(filename, "r", encoding="utf-8") as f:
40
+ locale_strs = json.load(f)
41
+ except FileNotFoundError:
42
+ locale_strs = {}
43
+ return locale_strs
44
+
45
+
46
+ def sort_strings(existing_translations):
47
+ # Sort the merged data
48
+ sorted_translations = {}
49
+ # Add entries with (NOT USED) in their values
50
+ for key, value in sorted(existing_translations.items(), key=lambda x: x[0]):
51
+ if "(🔴NOT USED)" in value:
52
+ sorted_translations[key] = value
53
+ # Add entries with empty values
54
+ for key, value in sorted(existing_translations.items(), key=lambda x: x[0]):
55
+ if value == "":
56
+ sorted_translations[key] = value
57
+ # Add the rest of the entries
58
+ for key, value in sorted(existing_translations.items(), key=lambda x: x[0]):
59
+ if value != "" and "(NOT USED)" not in value:
60
+ sorted_translations[key] = value
61
+
62
+ return sorted_translations
63
+
64
+
65
+ async def auto_translate(str, language):
66
+ headers = {
67
+ "Content-Type": "application/json",
68
+ "Authorization": f"Bearer {api_key}",
69
+ "temperature": f"{0}",
70
+ }
71
+ payload = {
72
+ "model": "gpt-3.5-turbo",
73
+ "messages": [
74
+ {
75
+ "role": "system",
76
+ "content": f"You are a translation program;\nYour job is to translate user input into {language};\nThe content you are translating is a string in the App;\nDo not explain emoji;\nIf input is only a emoji, please simply return origin emoji;\nPlease ensure that the translation results are concise and easy to understand."
77
+ },
78
+ {"role": "user", "content": f"{str}"}
79
+ ],
80
+ }
81
+
82
+ async with aiohttp.ClientSession() as session:
83
+ async with session.post(url, headers=headers, json=payload) as response:
84
+ data = await response.json()
85
+ return data["choices"][0]["message"]["content"]
86
+
87
+
88
+ async def main(auto=False):
89
+ current_strs = get_current_strings()
90
+ locale_files = []
91
+ # 遍历locale目录下的所有json文件
92
+ for dirpath, dirnames, filenames in os.walk("locale"):
93
+ for filename in filenames:
94
+ if filename.endswith(".json"):
95
+ locale_files.append(os.path.join(dirpath, filename))
96
+
97
+
98
+ for locale_filename in locale_files:
99
+ if "zh_CN" in locale_filename:
100
+ continue
101
+ locale_strs = get_locale_strings(locale_filename)
102
+
103
+ # Add new keys
104
+ new_keys = []
105
+ for key in current_strs:
106
+ if key not in locale_strs:
107
+ new_keys.append(key)
108
+ locale_strs[key] = ""
109
+ print(f"{locale_filename[7:-5]}'s new str: {len(new_keys)}")
110
+ # Add (NOT USED) to invalid keys
111
+ for key in locale_strs:
112
+ if key not in current_strs:
113
+ locale_strs[key] = "(🔴NOT USED)" + locale_strs[key]
114
+ print(f"{locale_filename[7:-5]}'s invalid str: {len(locale_strs) - len(current_strs)}")
115
+
116
+ locale_strs = sort_strings(locale_strs)
117
+
118
+ if auto:
119
+ tasks = []
120
+ non_translated_keys = []
121
+ for key in locale_strs:
122
+ if locale_strs[key] == "":
123
+ non_translated_keys.append(key)
124
+ tasks.append(auto_translate(key, locale_filename[7:-5]))
125
+ results = await asyncio.gather(*tasks)
126
+ for key, result in zip(non_translated_keys, results):
127
+ locale_strs[key] = "(🟡REVIEW NEEDED)" + result
128
+ print(f"{locale_filename[7:-5]}'s auto translated str: {len(non_translated_keys)}")
129
+
130
+ with open(locale_filename, 'w', encoding='utf-8') as f:
131
+ json.dump(locale_strs, f, ensure_ascii=False, indent=4)
132
+
133
+
134
+ if __name__ == "__main__":
135
+ auto = False
136
+ if len(sys.argv) > 1 and sys.argv[1] == "--auto":
137
+ auto = True
138
+ asyncio.run(main(auto))
locale/ja_JP.json CHANGED
@@ -1,87 +1,141 @@
1
  {
2
- "未命名对话历史记录": "名無しの会話履歴",
3
- "在这里输入": "ここに入力",
4
- "🧹 新的对话": "🧹 新しい会話",
5
- "🔄 重新生成": "🔄 再生成",
6
- "🗑️ 删除最旧对话": "🗑️ 最古の会話削除",
7
- "🗑️ 删除最新对话": "🗑️ 最新の会話削除",
8
- "模型": "LLMモデル",
9
- "多账号模式已开启,无需输入key,可直接开始对话": "複数アカウントモードがオンになっています。キーを入力する必要はありません。会話を開始できます",
10
  "**发送消息** 或 **提交key** 以显示额度": "**メッセージを送信** または **キーを送信** して、クレジットを表示します",
11
- "选择模型": "LLMモデルを選択",
12
- "选择LoRA模型": "LoRAモデルを選択",
13
- "实时传输回答": "ストリーム出力",
14
- "单轮对话": "単発会話",
15
- "使用在线搜索": "オンライン検索を使用",
16
- "选择回复语言(针对搜索&索引功能)": "回答言語を選択(検索とインデックス機能に対して)",
17
- "上传索引文件": "アップロード",
18
- "双栏pdf": "2カラムpdf",
19
- "识别公式": "formula OCR",
20
- "在这里输入System Prompt...": "System Promptを入力してください...",
21
- "加载Prompt模板": "Promptテンプレートを読込",
22
- "选择Prompt模板集合文件": "Promptテンプレートコレクションを選択",
23
- "🔄 刷新": "🔄 更新",
 
 
 
 
 
24
  "从Prompt模板中加载": "Promptテンプレートから読込",
25
- "保存/加载": "保存/読込",
26
- "保存/加载对话历史记录": "会話履歴を保存/読込",
27
  "从列表中加载对话": "リストから会話を読込",
28
- "设置文件名: 默认为.json,可选为.md": "ファイル名を設定: デフォルトは.json、.mdを選択できます",
29
- "设置保存文件名": "保存ファイル名を設定",
30
- "对话历史记录": "会話履歴",
31
- "💾 保存对话": "💾 会話を保存",
32
- "📝 导出为Markdown": "📝 Markdownでエクスポート",
33
- "默认保存于history文件夹": "デフォルトでhistoryフォルダに保存されます",
34
- "高级": "Advanced",
35
- "# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ 変更には慎重に ⚠️",
36
- "参数": "パラメータ",
37
- "停止符,用英文逗号隔开...": "ここにストップ文字を英語のカンマで区切って入力してください...",
38
- "用于定位滥用行为": "不正行為を特定するために使用されます",
39
- "用户名": "ユーザー名",
40
- "在这里输入API-Host...": "API-Hostを入力してください...",
41
- "🔄 切换API地址": "🔄 APIアドレスを切り替え",
42
- "未设置代理...": "代理が設定されていません...",
43
  "代理地址": "プロキシアドレス",
44
- "🔄 设置代理地址": "🔄 プロキシアドレスを設定",
45
- "🔙 恢复默认网络设置": "🔙 ネットワーク設定のリセット",
46
- "🔄 检查更新...": "🔄 アップデートをチェック...",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  "取消": "キャンセル",
48
- "更新": "アップデート",
49
- "详情": "詳細",
 
 
 
 
 
 
50
  "好": "はい",
51
- "更新成功,请重启本程序": "更新が成功しました、このプログラムを再起動してください",
52
- "更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "更新に失敗しました、[手動での更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)をお試しください。",
 
 
 
 
 
53
  "川虎Chat 🚀": "川虎Chat 🚀",
 
 
54
  "开始实时传输回答……": "ストリーム出力開始……",
55
- "Token 计数: ": "Token数: ",
56
- ",本次对话累计消耗了 ": ", 今の会話で消費合計 ",
57
- "**获取API使用情况失败**": "**API使用状況の取得に失敗しました**",
58
- "**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**API使用状況の取得に失敗しました**、`config.json`に正しい`sensitive_id`を入力する必要があります",
59
- "**获取API使用情况失败**,sensitive_id错误或已过期": "**API使用状況の取得に失敗しました**、sensitive_idが間違っているか、期限切れです",
60
- "**本月使用金额** ": "**今月の使用料金** ",
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  "本月使用金额": "今月の使用料金",
62
- "获取API使用情况失败:": "API使用状況の取得に失敗しました:",
63
- "API密钥更改为了": "APIキーが変更されました",
64
- "JSON解析错误,收到的内容: ": "JSON解析エラー、受信内容: ",
 
 
65
  "模型设置为了:": "LLMモデルを設定しました: ",
66
- "☹️发生了错误:": "エラーが発生しました: ",
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  "获取对话时发生错误,请查看后台日志": "会話取得時にエラー発生、あとのログを確認してください",
 
 
 
 
 
 
 
 
 
68
  "请检查网络连接,或者API-Key是否有效。": "ネットワーク接続を確認するか、APIキーが有効かどうかを確認してください。",
69
- "连接超时,无法获取对话。": "接続タイムアウト、会話を取得できません。",
70
- "读取超时,无法获取对话。": "読み込みタイムアウト、会話を取得できません。",
71
- "代理错误,无法获取对话。": "プロキシエラー、会話を取得できません。",
72
- "SSL错误,无法获取对话。": "SSLエラー、会話を取得できません。",
73
- "API key为空,请检查是否输入正确。": "APIキーが入力されていません。正しく入力されているか確認してください。",
74
  "请输入对话内容。": "会話内容を入力してください。",
 
 
75
  "账单信息不适用": "課金情報は対象外です",
76
- "由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "開発:Bilibili [土川虎虎虎](https://space.bilibili.com/29125536) と [明昭MZhao](https://space.bilibili.com/24807452) と [Keldos](https://github.com/Keldos-Li)\n\n最新コードは川虎Chatのサイトへ [GitHubプロジェクト](https://github.com/GaiZhenbiao/ChuanhuChatGPT)",
77
- "切换亮暗色主题": "テーマの明暗切替",
78
- "您的IP区域:未知。": "あなたのIPアドレス地域:不明",
79
- "获取IP地理位置失败。原因:": "IPアドレス地域の取得に失敗しました。理由:",
80
- "。你仍然可以使用聊天功能。": "。あなたはまだチャット機能を使用できます。",
81
- "您的IP区域:": "あなたのIPアドレス地域:",
82
- "总结": "要約する",
83
- "生成内容总结中……": "コンテンツ概要を生成しています...",
84
- "由于下面的原因,Google 拒绝返回 PaLM 的回答:\n\n": "Googleは以下の理由から、PaLMの回答を返すことを拒否しています:\n\n",
85
- "---\n⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "---\n⚠️ APIキーの安全性を確保するために、`config.json`ファイルでネットワーク設定を変更してください。",
86
- "网络参数": "ネットワークパラメータ"
87
- }
 
 
 
 
 
 
 
 
 
 
 
1
  {
2
+ " 吗?": " を削除してもよろしいですか?",
3
+ "# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ 変更には慎重に ⚠️",
 
 
 
 
 
 
4
  "**发送消息** 或 **提交key** 以显示额度": "**メッセージを送信** または **キーを送信** して、クレジットを表示します",
5
+ "**本月使用金额** ": "**今月の使用料金** ",
6
+ "**获取API使用情况失败**": "**API使用状況の取得に失敗しました**",
7
+ "**获取API使用情况失败**,sensitive_id错误或已过期": "**API使用状況の取得に失敗しました**、sensitive_idが間違っているか、期限切れです",
8
+ "**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**API使用状況の取得に失敗しました**、`config.json`に正しい`sensitive_id`を入力する必要があります",
9
+ "API key为空,请检查是否输入正确。": "APIキーが入力されていません。正しく入力されているか確認してください。",
10
+ "API密钥更改为了": "APIキーが変更されました",
11
+ "JSON解析错误,收到的内容: ": "JSON解析エラー、受信内容: ",
12
+ "SSL错误,无法获取对话。": "SSLエラー、会話を取得できません。",
13
+ "Token 计数: ": "Token数: ",
14
+ "☹️发生了错误:": "エラーが発生しました: ",
15
+ "⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "⚠️ APIキーの安全性を確保するために、`config.json`ファイルでネットワーク設定を変更してください。",
16
+ "。你仍然可以使用聊天功能。": "。あなたはまだチャット機能を使用できます。",
17
+ "上传": "アップロード",
18
+ "上传了": "アップロードしました。",
19
+ "上传到 OpenAI 后自动填充": "OpenAIへのアップロード後、自動的に入力されます",
20
+ "上传到OpenAI": "OpenAIへのアップロード",
21
+ "上传文件": "ファイルをアップロード",
22
+ "仅供查看": "閲覧専用",
23
  "从Prompt模板中加载": "Promptテンプレートから読込",
 
 
24
  "从列表中加载对话": "リストから会話を読込",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  "代理地址": "プロキシアドレス",
26
+ "代理��误,无法获取对话。": "プロキシエラー、会話を取得できません。",
27
+ "你没有权限访问 GPT4,[进一步了解](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)": "GPT-4にアクセス権がありません、[詳細はこちら](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)",
28
+ "你没有选择任何对话历史": "あなたは何の会話履歴も選択していません。",
29
+ "你真的要删除 ": "本当に ",
30
+ "使用在线搜索": "オンライン検索を使用",
31
+ "停止符,用英文逗号隔开...": "ここにストップ文字を英語のカンマで区切って入力してください...",
32
+ "关于": "について",
33
+ "准备数据集": "データセットの準備",
34
+ "切换亮暗色主题": "テーマの明暗切替",
35
+ "删除对话历史成功": "削除した会話の履歴",
36
+ "删除这轮问答": "この質疑応答を削除",
37
+ "刷新状态": "ステータスを更新",
38
+ "剩余配额不足,[进一步了解](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)": "剩余配额不足,[进一步了解](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)",
39
+ "加载Prompt模板": "Promptテンプレートを読込",
40
+ "单轮对话": "単発会話",
41
+ "历史记录(JSON)": "履歴ファイル(JSON)",
42
+ "参数": "パラメータ",
43
+ "双栏pdf": "2カラムpdf",
44
  "取消": "キャンセル",
45
+ "取消所有任务": "すべてのタスクをキャンセル",
46
+ "可选,用于区分不同的模型": "オプション、異なるモデルを区別するために使用",
47
+ "启用的工具:": "有効なツール:",
48
+ "在工具箱中管理知识库文件": "ツールボックスでナレッジベースファイルの管理を行う",
49
+ "在线搜索": "オンライン検索",
50
+ "在这里输入": "ここに入力",
51
+ "在这里输入System Prompt...": "System Promptを入力してください...",
52
+ "多账号模式已开启,无需输入key,可直接开始对话": "複数アカウントモードがオンになっています。キーを入力する必要はありません。会話を開始できます",
53
  "好": "はい",
54
+ "实时传输回答": "ストリーム出力",
55
+ "对话": "会話",
56
+ "对话历史": "対話履歴",
57
+ "对话历史记录": "会話履歴",
58
+ "对话命名方式": "会話の命名方法",
59
+ "导出为 Markdown": "Markdownでエクスポート",
60
+ "川虎Chat": "川虎Chat",
61
  "川虎Chat 🚀": "川虎Chat 🚀",
62
+ "工具箱": "ツールボックス",
63
+ "已经被删除啦": "削除されました。",
64
  "开始实时传输回答……": "ストリーム出力開始……",
65
+ "开始训练": "トレーニングを開始",
66
+ "微调": "ファインチューニング",
67
+ "总结": "要約する",
68
+ "总结完成": "完了",
69
+ "您使用的就是最新版!": "最新バージョンを使用しています!",
70
+ "您的IP区域:": "あなたのIPアドレス地域:",
71
+ "您的IP区域:未知。": "あなたのIPアドレス地域:不明",
72
+ "拓展": "拡張",
73
+ "搜索(支持正则)...": "検索(正規表現をサポート)...",
74
+ "数据集预览": "データセットのプレビュー",
75
+ "文件ID": "ファイルID",
76
+ "新对话 ": "新しい会話 ",
77
+ "新建对话保留Prompt": "新しい会話を作成してください。プロンプトを保留します。",
78
+ "暂时未知": "しばらく不明である",
79
+ "更新": "アップデート",
80
+ "更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "更新に失敗しました、[手動での更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)をお試しください。",
81
+ "更新成功,请重启本程序": "更新が成功しました、このプログラムを再起動してください",
82
+ "未命名对话历史记录": "名無しの会話履歴",
83
+ "未设置代理...": "代理が設定されていません...",
84
  "本月使用金额": "今月の使用料金",
85
+ "查看[使用介绍](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35)": "[使用ガイド](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35)を表示",
86
+ "根据日期时间": "日付と時刻に基づいて",
87
+ "模型": "LLMモデル",
88
+ "模型名称后缀": "モデル名のサフィックス",
89
+ "模型自动总结(消耗tokens)": "モデルによる自動要約(トークン消費)",
90
  "模型设置为了:": "LLMモデルを設定しました: ",
91
+ "正在尝试更新...": "更新を試みています...",
92
+ "添加训练好的模型到模型列表": "トレーニング済みモデルをモデルリストに追加",
93
+ "状态": "ステータス",
94
+ "生成内容总结中……": "コンテンツ概要を生成しています...",
95
+ "用于定位滥用行为": "不正行為を特定するために使用されます",
96
+ "用户名": "ユーザー名",
97
+ "由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "開発:Bilibili [土川虎虎虎](https://space.bilibili.com/29125536) と [明昭MZhao](https://space.bilibili.com/24807452) と [Keldos](https://github.com/Keldos-Li)\n\n最新コードは川虎Chatのサイトへ [GitHubプロジェクト](https://github.com/GaiZhenbiao/ChuanhuChatGPT)",
98
+ "知识库": "ナレッジベース",
99
+ "知识库文件": "ナレッジベースファイル",
100
+ "第一条提问": "最初の質問",
101
+ "索引构建完成": "索引の構築が完了しました。",
102
+ "网络": "ネットワーク",
103
+ "获取API使用情况失败:": "API使用状況の取得に失敗しました:",
104
+ "获取IP地理位置失败。原因:": "IPアドレス地域の取得に失敗しました。理由:",
105
  "获取对话时发生错误,请查看后台日志": "会話取得時にエラー発生、あとのログを確認してください",
106
+ "训练": "トレーニング",
107
+ "训练状态": "トレーニングステータス",
108
+ "训练轮数(Epochs)": "トレーニングエポック数",
109
+ "设置": "設定",
110
+ "设置保存文件名": "保存ファイル名を設定",
111
+ "设置文件名: 默认为.json,可选为.md": "ファイル名を設定: デフォルトは.json、.mdを選択できます",
112
+ "识别公式": "formula OCR",
113
+ "详情": "詳細",
114
+ "请查看 config_example.json,配置 Azure OpenAI": "Azure OpenAIの設定については、config_example.jsonをご覧ください",
115
  "请检查网络连接,或者API-Key是否有效。": "ネットワーク接続を確認するか、APIキーが有効かどうかを確認してください。",
 
 
 
 
 
116
  "请输入对话内容。": "会話内容を入力してください。",
117
+ "请输入有效的文件名,不要包含以下特殊字符:": "有効なファイル名を入力してください。以下の特殊文字は使用しないでください:",
118
+ "读取超时,无法获取对话。": "読み込みタイムアウト、会話を取得できません。",
119
  "账单信息不适用": "課金情報は対象外です",
120
+ "连接超时,无法获取对话。": "接続タイムアウト、会話を取得できません。",
121
+ "选择LoRA模型": "LoRAモデルを選択",
122
+ "选择Prompt模板集合文件": "Promptテンプレートコレクションを選択",
123
+ "选择回复语言(针对搜索&索引功能)": "回答言語を選択(検索とインデックス機能に対して)",
124
+ "选择数据集": "データセットの選択",
125
+ "选择模型": "LLMモデルを選択",
126
+ "重命名该对话": "会話の名前を変更",
127
+ "重新生成": "再生成",
128
+ "高级": "Advanced",
129
+ ",本次对话累计消耗了 ": ", 今の会話で消費合計 ",
130
+ "💾 保存对话": "💾 会話を保存",
131
+ "📝 导出为 Markdown": "📝 Markdownにエクスポート",
132
+ "🔄 切换API地址": "🔄 APIアドレスを切り替え",
133
+ "🔄 刷新": "🔄 更新",
134
+ "🔄 检查更新...": "🔄 アップデートをチェック...",
135
+ "🔄 设置代理地址": "🔄 プロキシアドレスを設定",
136
+ "🔄 重新生成": "🔄 再生成",
137
+ "🔙 恢复默认网络设置": "🔙 ネットワーク設定のリセット",
138
+ "🗑️ 删除最新对话": "🗑️ 最新の会話削除",
139
+ "🗑️ 删除最旧对话": "🗑️ 最古の会話削除",
140
+ "🧹 新的对话": "🧹 新しい会話"
141
+ }
locale/ko_KR.json CHANGED
@@ -1,89 +1,141 @@
1
  {
2
- "未命名对话历史记录": "이름없는 대화 기록",
3
- "在这里输入": "여기에 입력하세요",
4
- "🧹 新的对话": "🧹 새로운 대화",
5
- "🔄 重新生成": "🔄 재생성",
6
- "🗑️ 删除最旧对话": "🗑️ 가장 오래된 대화 삭제",
7
- "🗑️ 删除最新对话": "🗑️ 최신 대화 삭제",
8
- "🗑️ 删除": "🗑️ 삭제",
9
- "模型": "LLM 모델",
10
- "多账号模式已开启,无需输入key,可直接开始对话": "다중 계정 모드가 활성화되어 있으므로 키를 입력할 필요가 없이 바로 대화를 시작할 수 있습니다",
11
  "**发送消息** 或 **提交key** 以显示额度": "**메세지를 전송** 하거나 **Key를 입력**하여 크레딧 표시",
12
- "选择模型": "모델 선택",
13
- "选择LoRA模型": "LoRA 모델 선택",
14
- "实时传输回答": "실시간 전송",
15
- "单轮对话": "단일 대화",
16
- "使用在线搜索": "온라인 검색 사용",
17
- "选择回复语言(针对搜索&索引功能)": "답장 언어 선택 (검색 & 인덱스용)",
18
- "上传索引文件": "업로드",
19
- "双栏pdf": "2-column pdf",
20
- "识别公式": "formula OCR",
21
- "在这里输入System Prompt...": "여기에 시스템 프롬프트를 입력하세요...",
22
- "加载Prompt模板": "프롬프트 템플릿 불러오기",
23
- "选择Prompt模板集合文件": "프롬프트 콜렉션 파일 선택",
24
- "🔄 刷新": "🔄 새로고침",
 
 
 
 
 
25
  "从Prompt模板中加载": "프롬프트 템플릿에서 불러오기",
26
- "保存/加载": "저장/불러오기",
27
- "保存/加载对话历史记录": "대화 기록 저장/불러오기",
28
  "从列表中加载对话": "리스트에서 대화 불러오기",
29
- "设置文件名: 默认为.json,可选为.md": "파일 이름 설정: 기본값: .json, 선택: .md",
30
- "设置保存文件名": "저장 파일명 설정",
31
- "对话历史记录": "대화 기록",
32
- "💾 保存对话": "💾 대화 저장",
33
- "📝 导出为Markdown": "📝 마크다운으로 내보내기",
34
- "默认保存于history文件夹": "히스토리 폴더에 기본 저장",
35
- "高级": "고급",
36
- "# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ 주의: 변경시 주의하세요. ⚠️",
37
- "参数": "파라미터들",
38
- "停止符,用英文逗号隔开...": "여기에 정지 토큰 입력, ','로 구분됨...",
39
- "用于定位滥用行为": "악용 사례 파악에 활용됨",
40
- "用户名": "사용자 이름",
41
- "在这里输入API-Host...": "여기에 API host를 입력하세요...",
42
- "🔄 切换API地址": "🔄 API 주소 변경",
43
- "未设置代理...": "대리인이 설정되지 않았습니다...",
44
  "代理地址": "프록시 주소",
45
- "🔄 设置代理地址": "🔄 프록시 주소 설정",
46
- "🔙 恢复默认网络设置": "🔙 네트워크 설정 초기화",
47
- "🔄 检查更新...": "🔄 업데이트 확인...",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  "取消": "취소",
49
- "更新": "업데이트",
50
- "详情": "상세",
 
 
 
 
 
 
51
  "好": "예",
52
- "更新成功,请重启本程序": "업데이트 성공, 이 프로그램을 재시작 해주세요",
53
- "更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "업데이트 실패, [수동 업데이트](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)를 시도하십시오",
 
 
 
 
 
54
  "川虎Chat 🚀": "Chuanhu Chat 🚀",
 
 
55
  "开始实时传输回答……": "실시간 응답 출력 시작...",
56
- "Token 计数: ": "토큰 수: ",
57
- ",本次对话累计消耗了 ": ",이 대화의 전체 비용은 ",
58
- "**获取API使用情况失败**": "**API 사용량 가져오기 실패**",
59
- "**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**API 사용량 가져오기 실패**. `config.json`에 올바른 `sensitive_id`를 입력해야 합니다",
60
- "**获取API使用情况失败**,sensitive_id错误或已过期": "**API 사용량 가져오기 실패**. sensitive_id가 잘못되었거나 만료되었습니다",
61
- "**本月使用金额** ": "**이번 사용금액** ",
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  "本月使用金额": "이번 달 사용금액",
63
- "获取API使用情况失败:": "API 사용량 가져오기 실패:",
64
- "API密钥更改为了": "API 키가 변경되었습니다.",
65
- "JSON解析错误,收到的内容: ": "JSON 파싱 에러, 응답: ",
 
 
66
  "模型设置为了:": "설정된 모델: ",
67
- "☹️发生了错误:": "☹️에러: ",
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  "获取对话时发生错误,请查看后台日志": "대화를 가져오는 중 에러가 발생했습니다. 백그라운드 로그를 확인하세요",
 
 
 
 
 
 
 
 
 
69
  "请检查网络连接,或者API-Key是否有效。": "네트워크 연결 또는 API키가 유효한지 확인하세요",
70
- "连接超时,无法获取对话。": "연결 시간 초과, 대화를 가져올 수 없습니다.",
71
- "读取超时,无法获取对话。": "읽기 시간 초과, 대화를 가져올 수 없습니다.",
72
- "代理错误,无法获取对话。": "프록시 에러, 대화를 가져올 수 없습니다.",
73
- "SSL错误,无法获取对话。": "SSL 에러, 대화를 가져올 수 없습니다.",
74
- "API key为空,请检查是否输入正确。": "API 키가 비어 있습니다. 올바르게 입력되었는지 확인하십세요.",
75
  "请输入对话内容。": "대화 내용을 입력하세요.",
 
 
76
  "账单信息不适用": "청구 정보를 가져올 수 없습니다",
77
- "由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "제작: Bilibili [土川虎虎虎](https://space.bilibili.com/29125536), [明昭MZhao](https://space.bilibili.com/24807452), [Keldos](https://github.com/Keldos-Li)\n\n최신 코드 다운로드: [GitHub](https://github.com/GaiZhenbiao/ChuanhuChatGPT)",
78
- "切换亮暗色主题": "라이트/다크 테마 전환",
79
- "您的IP区域:未知。": "IP 지역: 수 없음.",
80
- "获取IP地理位置失败。原因:": "다음과 같은 이유로 IP 위치를 가져올 수 없습니다. 이유: ",
81
- "。你仍然可以使用聊天功能。": ". 채팅 기능을 계속 사용할 수 있습니다.",
82
- "您的IP区域:": "당신의 IP 지역: ",
83
- "总结": "요약",
84
- "生成内容总结中……": "콘텐츠 요약 생성중...",
85
- "上传": "업로드",
86
- "由于下面的原因,Google 拒绝返回 PaLM 的回答:\n\n": "구글은 다음과 같은 이유로 인해 PaLM의 응답을 거부합니다: \n\n",
87
- "---\n⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "---\n⚠️ API-Key의 안전을 보장하기 위해 네트워크 설정을 `config.json` 구성 파일에서 수정해주세요.",
88
- "网络参数": "네트워크 매개변수"
89
- }
 
 
 
 
 
 
 
 
 
 
1
  {
2
+ " 吗?": " 을(를) 삭제하시겠습니까?",
3
+ "# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ 주의: 변경시 주의하세요. ⚠️",
 
 
 
 
 
 
 
4
  "**发送消息** 或 **提交key** 以显示额度": "**메세지를 전송** 하거나 **Key를 입력**하여 크레딧 표시",
5
+ "**本月使用金额** ": "**이번 달 사용금액** ",
6
+ "**获取API使用情况失败**": "**API 사용량 가져오기 실패**",
7
+ "**获取API使用情况失败**,sensitive_id错误或已过期": "**API 사용량 가져오기 실패**. sensitive_id가 잘못되었거나 만료되었습니다",
8
+ "**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**API 사용량 가져오기 실패**. `config.json`에 올바른 `sensitive_id`를 입력해야 합니다",
9
+ "API key为空,请检查是否输入正确。": "API 키가 비어 있습니다. 올바르게 입력되었는지 확인하십세요.",
10
+ "API密钥更改为了": "API 키가 변경되었습니다.",
11
+ "JSON解析错误,收到的内容: ": "JSON 파싱 에러, 응답: ",
12
+ "SSL错误,无法获取对话。": "SSL 에러, 대화를 가져올 수 없습니다.",
13
+ "Token 计数: ": "토큰 수: ",
14
+ "☹️发生了错误:": "☹️에러: ",
15
+ "⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "⚠️ API-Key의 안전을 보장하기 위해 네트워크 설정을 `config.json` 구성 파일에서 수정해주세요.",
16
+ "。你仍然可以使用聊天功能。": ". 채팅 기능을 계속 사용할 수 있습니다.",
17
+ "上传": "업로드",
18
+ "上传了": "업로드되었습니다.",
19
+ "上传到 OpenAI 后自动填充": "OpenAI로 업로드한 후 자동으로 채워집니다",
20
+ "上传到OpenAI": "OpenAI로 업로드",
21
+ "上传文件": "파일 업로드",
22
+ "仅供查看": "읽기 전용",
23
  "从Prompt模板中加载": "프롬프트 템플릿에서 불러오기",
 
 
24
  "从列表中加载对话": "리스트에서 대화 불러오기",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  "代理地址": "프록시 주소",
26
+ "代理错误,无法获取对话。": "프록시 에러, 대화를 가져올 수 없습니다.",
27
+ "你没有权限访问 GPT4,[进一步了解](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)": "GPT-4에 접근 권한이 없습니다. [자세히 알아보기](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)",
28
+ "你没有选择任何对话历史": "대화 기록을 선택하지 않았습니다.",
29
+ "你真的要删除 ": "정말로 ",
30
+ "使用在线搜索": "온라인 검색 사용",
31
+ "停止符,用英文逗号隔开...": "여기에 정지 토큰 입력, ','로 구분됨...",
32
+ "关于": "관련",
33
+ "准备数据集": "데이터셋 준비",
34
+ "切换亮暗色主题": "라이트/다크 테마 전환",
35
+ "删除对话历史成功": "대화 기록이 성공적으로 삭제되었습니다.",
36
+ "删除这轮问答": "이 라운드의 질문과 답변 삭제",
37
+ "刷新状态": "상태 새로 고침",
38
+ "剩余配额不足,[进一步了解](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)": "남은 할당량이 부족합니다. [자세한 내용](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)을 확인하세요.",
39
+ "加载Prompt模板": "프롬프트 템플릿 불러오기",
40
+ "单轮对话": "단일 대화",
41
+ "历史记录(JSON)": "기록 파일 (JSON)",
42
+ "参数": "파라미터들",
43
+ "双栏pdf": "2-column pdf",
44
  "取消": "취소",
45
+ "取消所有任务": "모든 작업 취소",
46
+ "可选,用于区分不同的模型": "선택 사항, 다른 모델을 구분하는 데 사용",
47
+ "启用的工具:": "활성화된 도구: ",
48
+ "在工具箱中管理知识库文件": "지식 라이브러리 파일을 도구 상자에서 관리",
49
+ "在线搜索": "온라인 검색",
50
+ "在这里输入": "여기에 입력하세요",
51
+ "在这里输入System Prompt...": "여기에 시스템 프롬프트를 입력하세요...",
52
+ "多账号模式已开启,无需输入key,可直接开始对话": "다중 계정 모드가 활성화되어 있으므로 키를 입력할 필요가 없이 바로 대화를 시작할 수 있습니다",
53
  "好": "예",
54
+ "实时传输回答": "실시간 전송",
55
+ "对话": "대화",
56
+ "对话历史": "대화 내역",
57
+ "对话历史记录": "대화 기록",
58
+ "对话命名方式": "대화 이름 설정",
59
+ "导出为 Markdown": "마크다운으로 내보내기",
60
+ "川虎Chat": "Chuanhu Chat",
61
  "川虎Chat 🚀": "Chuanhu Chat 🚀",
62
+ "工具箱": "도구 상자",
63
+ "已经被删除啦": "이미 삭제되었습니다.",
64
  "开始实时传输回答……": "실시간 응답 출력 시작...",
65
+ "开始训练": "훈련 시작",
66
+ "微调": "미세 조정",
67
+ "总结": "요약",
68
+ "总结完成": "작업 완료",
69
+ "您使用的就是最新版!": "최신 버전을 사용하고 있습니다!",
70
+ "您的IP区域:": "당신의 IP 지역: ",
71
+ "您的IP区域:未知。": "IP 지역: 알 수 없음.",
72
+ "拓展": "확장",
73
+ "搜索(支持正则)...": "검색 (정규식 지원)...",
74
+ "数据集预览": "데이터셋 미리보기",
75
+ "文件ID": "파일 ID",
76
+ "新对话 ": "새 대화 ",
77
+ "新建对话保留Prompt": "새 대화 생성, 프롬프트 유지하기",
78
+ "暂时未知": "알 수 없음",
79
+ "更新": "업데이트",
80
+ "更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "업데이트 실패, [수동 업데이트](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)를 시도하십시오",
81
+ "更新成功,请重启本程序": "업데이트 성공, 이 프로그램을 재시작 해주세요",
82
+ "未命名对话历史记录": "이름없는 대화 기록",
83
+ "未设置代理...": "대리인이 설정되지 않았습니다...",
84
  "本月使用金额": "이번 달 사용금액",
85
+ "查看[使用介绍](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35)": "[사용 가이드](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35) 보기",
86
+ "根据日期时间": "날짜 시간 기준",
87
+ "模型": "LLM 모델",
88
+ "模型名称后缀": "모델 이름 접미사",
89
+ "模型自动总结(消耗tokens)": "모델에 의한 자동 요약 (토큰 소비)",
90
  "模型设置为了:": "설정된 모델: ",
91
+ "正在尝试更新...": "업데이트를 시도 중...",
92
+ "添加训练好的模型到模型列表": "훈련된 모델을 모델 목록에 추가",
93
+ "状态": "상태",
94
+ "生成内容总结中……": "콘텐츠 요약 생성중...",
95
+ "用于定位滥用行为": "악용 사례 파악에 활용됨",
96
+ "用户名": "사용자 이름",
97
+ "由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "제작: Bilibili [土川虎虎虎](https://space.bilibili.com/29125536), [明昭MZhao](https://space.bilibili.com/24807452), [Keldos](https://github.com/Keldos-Li)\n\n최신 코드 다운로드: [GitHub](https://github.com/GaiZhenbiao/ChuanhuChatGPT)",
98
+ "知识库": "지식 라이브러리",
99
+ "知识库文件": "지식 라이브러리 파일",
100
+ "第一条提问": "첫 번째 질문",
101
+ "索引构建完成": "인덱스 구축이 완료되었습니다.",
102
+ "网络": "네트워크",
103
+ "获取API使用情况失败:": "API 사용량 가져오기 실패:",
104
+ "获取IP地理位置失败。原因:": "다음과 같은 이유로 IP 위치를 가져올 수 없습니다. 이유: ",
105
  "获取对话时发生错误,请查看后台日志": "대화를 가져오는 중 에러가 발생했습니다. 백그라운드 로그를 확인하세요",
106
+ "训练": "훈련",
107
+ "训练状态": "훈련 상태",
108
+ "训练轮数(Epochs)": "훈련 라운드(Epochs)",
109
+ "设置": "설정",
110
+ "设置保存文件名": "저장 파일명 설정",
111
+ "设置文件名: 默认为.json,可选为.md": "파일 이름 설정: 기본값: .json, 선택: .md",
112
+ "识别公式": "formula OCR",
113
+ "详情": "상세",
114
+ "请查看 config_example.json,配置 Azure OpenAI": "Azure OpenAI 설정을 확인하세요",
115
  "请检查网络连接,或者API-Key是否有效。": "네트워크 연결 또는 API키가 유효한지 확인하세요",
 
 
 
 
 
116
  "请输入对话内容。": "대화 내용을 입력하세요.",
117
+ "请输入有效的文件名,不要包含以下特殊字符:": "유효한 파일 이름을 입력하세요. 다음 특수 문자는 포함하지 마세요: ",
118
+ "读取超时,无法获取对话。": "읽기 시간 초과, 대화를 가져올 수 없습니다.",
119
  "账单信息不适用": "청구 정보를 가져올 수 없습니다",
120
+ "连接超时,无法获取对话。": "연결 시간 초과, 대화를 가져올 없습니다.",
121
+ "选择LoRA模型": "LoRA 모델 선택",
122
+ "选择Prompt模板集合文件": "프롬프트 콜렉션 파일 선택",
123
+ "选择回复语言(针对搜索&索引功能)": "답장 언어 선택 (검색 & 인덱스용)",
124
+ "选择数据集": "데이터셋 선택",
125
+ "选择模型": "모델 선택",
126
+ "重命名该对话": "대화 이름 변경",
127
+ "重新生成": "재생성",
128
+ "高级": "고급",
129
+ ",本次对话累计消耗了 ": ",이 대화의 전체 비용은 ",
130
+ "💾 保存对话": "💾 대화 저장",
131
+ "📝 导出为 Markdown": "📝 마크다운으로 내보내기",
132
+ "🔄 切换API地址": "🔄 API 주소 변경",
133
+ "🔄 刷新": "🔄 새로고침",
134
+ "🔄 检查更新...": "🔄 업데이트 확인...",
135
+ "🔄 设置代理地址": "🔄 프록시 주소 설정",
136
+ "🔄 重新生成": "🔄 재생성",
137
+ "🔙 恢复默认网络设置": "🔙 네트워크 설정 초기화",
138
+ "🗑️ 删除最新对话": "🗑️ 최신 대화 삭제",
139
+ "🗑️ 删除最旧对话": "🗑️ 가장 오래된 대화 삭제",
140
+ "🧹 新的对话": "🧹 새로운 대화"
141
+ }
locale/ru_RU.json ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ " 吗?": " ?",
3
+ "# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ ВНИМАНИЕ: ИЗМЕНЯЙТЕ ОСТОРОЖНО ⚠️",
4
+ "**发送消息** 或 **提交key** 以显示额度": "**Отправить сообщение** или **отправить ключ** для отображения лимита",
5
+ "**本月使用金额** ": "**Использовано средств в этом месяце**",
6
+ "**获取API使用情况失败**": "**Не удалось получить информацию об использовании API**",
7
+ "**获取API使用情况失败**,sensitive_id错误或已过期": "**Не удалось получить информацию об использовании API**, ошибка sensitive_id или истек срок действия",
8
+ "**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**Не удалось получить информацию об использовании API**, необходимо правильно заполнить sensitive_id в `config.json`",
9
+ "API key为空,请检查是否输入正确。": "Пустой API-Key, пожалуйста, проверьте правильность ввода.",
10
+ "API密钥更改为了": "Ключ API изменен на",
11
+ "JSON解析错误,收到的内容: ": "Ошибка анализа JSON, полученный контент:",
12
+ "SSL错误,无法获取对话。": "Ошибка SSL, не удалось получить диалог.",
13
+ "Token 计数: ": "Использованно токенов: ",
14
+ "☹️发生了错误:": "☹️ Произошла ошибка:",
15
+ "⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "⚠️ Для обеспечения безопасности API-Key, измените настройки сети в файле конфигурации `config.json`",
16
+ "。你仍然可以使用聊天功能。": ". Вы все равно можете использовать функцию чата.",
17
+ "上传": "Загрузить",
18
+ "上传了": "Загрузка завершена.",
19
+ "上传到 OpenAI 后自动填充": "Автоматическое заполнение после загрузки в OpenAI",
20
+ "上传到OpenAI": "Загрузить в OpenAI",
21
+ "上传文件": "Загрузить файл",
22
+ "仅供查看": "Только для просмотра",
23
+ "从Prompt模板中加载": "Загрузить из шаблона Prompt",
24
+ "从列表中加载对话": "Загрузить диалог из списка",
25
+ "代理地址": "Адрес прокси",
26
+ "代理错误,无法获取对话。": "Ошибка прокси, не удалось получить диалог.",
27
+ "你没有权限访问 GPT4,[进一步了解](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)": "У вас нет доступа к GPT4, [подробнее](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)",
28
+ "你没有选择任何对话历史": "Вы не выбрали никакой истории переписки",
29
+ "你真的要删除 ": "Вы уверены, что хотите удалить ",
30
+ "使用在线搜索": "Использовать онлайн-поиск",
31
+ "停止符,用英文逗号隔开...": "Разделительные символы, разделенные запятой...",
32
+ "关于": "О программе",
33
+ "准备数据集": "Подготовка набора данных",
34
+ "切换亮暗色主题": "Переключить светлую/темную тему",
35
+ "删除对话历史成功": "Успешно удалена история переписки.",
36
+ "删除这轮问答": "Удалить этот раунд вопросов и ответов",
37
+ "刷新状态": "Обновить статус",
38
+ "剩余配额不足,[进一步了解](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)": "剩余配额不足,[进一步了解](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)",
39
+ "加载Prompt模板": "Загрузить шаблон Prompt",
40
+ "单轮对话": "Одиночный диалог",
41
+ "历史记录(JSON)": "Файл истории (JSON)",
42
+ "参数": "Параметры",
43
+ "双栏pdf": "Двухколоночный PDF",
44
+ "取消": "Отмена",
45
+ "取消所有任务": "Отменить все задачи",
46
+ "可选,用于区分不同的模型": "Необязательно, используется для различения разных моделей",
47
+ "启用的工具:": "Включенные инструменты:",
48
+ "在工具箱中管理知识库文件": "Управление файлами базы знаний в инструментах",
49
+ "在线搜索": "Онлайн-поиск",
50
+ "在这里输入": "Введите здесь",
51
+ "在这里输入System Prompt...": "Введите здесь системное подсказку...",
52
+ "多账号模式已开启,无需输入key,可直接开始对话": "Режим множественных аккаунтов включен, не требуется ввод ключа, можно сразу начать диалог",
53
+ "好": "Хорошо",
54
+ "实时传输回答": "Передача ответа в реальном времени",
55
+ "对话": "Диалог",
56
+ "对话历史": "Диалоговая история",
57
+ "对话历史记录": "История диалога",
58
+ "对话命名方式": "Способ названия диалога",
59
+ "导出为 Markdown": "Экспортировать в Markdown",
60
+ "川虎Chat": "Chuanhu Чат",
61
+ "川虎Chat 🚀": "Chuanhu Чат 🚀",
62
+ "工具箱": "Инструменты",
63
+ "已经被删除啦": "Уже удалено.",
64
+ "开始实时传输回答……": "Начните трансляцию ответов в режиме реального времени...",
65
+ "开始训练": "Начать обучение",
66
+ "微调": "Своя модель",
67
+ "总结": "Подведение итога",
68
+ "总结完成": "Готово",
69
+ "您使用的就是最新版!": "Вы используете последнюю версию!",
70
+ "您的IP区域:": "Ваша IP-зона:",
71
+ "您的IP区域:未知。": "Ваша IP-зона: неизвестно.",
72
+ "拓展": "Расширенные настройки",
73
+ "搜索(支持正则)...": "Поиск (поддержка регулярности)...",
74
+ "数据集预览": "Предпросмотр набора данных",
75
+ "文件ID": "Идентификатор файла",
76
+ "新对话 ": "Новый диалог ",
77
+ "新建对话保留Prompt": "Создать диалог с сохранением подсказки",
78
+ "暂时未知": "Временно неизвестно",
79
+ "更新": "Обновить",
80
+ "更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "Обновление не удалось, пожалуйста, попробуйте обновить вручную",
81
+ "更新成功,请重启本程序": "Обновление успешно, пожалуйста, перезапустите программу",
82
+ "未命名对话历史记录": "Безымянная история диалога",
83
+ "未设置代理...": "Прокси не настроен...",
84
+ "本月使用金额": "Использовано средств в этом месяце",
85
+ "查看[使用介绍](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35)": "[Здесь](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35) можно ознакомиться с инструкцией по использованию",
86
+ "根据日期时间": "По дате и времени",
87
+ "模型": "Модель",
88
+ "模型名称后缀": "Суффикс имени модели",
89
+ "模型自动总结(消耗tokens)": "Автоматическое подведение итогов модели (потребление токенов)",
90
+ "模型设置为了:": "Модель настроена на:",
91
+ "正在尝试更新...": "Попытка обновления...",
92
+ "添加训练好的模型到模型列表": "Добавить обученную модель в список моделей",
93
+ "状态": "Статус",
94
+ "生成内容总结中……": "Создание сводки контента...",
95
+ "用于定位滥用行为": "Используется для выявления злоупотреблений",
96
+ "用户名": "Имя пользователя",
97
+ "由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "Разработано [土川虎虎虎](https://space.bilibili.com/29125536), [明昭MZhao](https://space.bilibili.com/24807452) и [Keldos](https://github.com/Keldos-Li).<br />посетите [GitHub Project](https://github.com/GaiZhenbiao/ChuanhuChatGPT) чата Chuanhu, чтобы загрузить последнюю версию скрипта",
98
+ "知识库": "База знаний",
99
+ "知识库文件": "Файл базы знаний",
100
+ "第一条提问": "Первый вопрос",
101
+ "索引构建完成": "Индексирование завершено.",
102
+ "网络": "Параметры сети",
103
+ "获取API使用情况失败:": "Не удалось получитьAPIинформацию об использовании:",
104
+ "获取IP地理位置失��。原因:": "Не удалось получить географическое положение IP. Причина:",
105
+ "获取对话时发生错误,请查看后台日志": "Возникла ошибка при получении диалога, пожалуйста, проверьте журналы",
106
+ "训练": "Обучение",
107
+ "训练状态": "Статус обучения",
108
+ "训练轮数(Epochs)": "Количество эпох обучения",
109
+ "设置": "Настройки",
110
+ "设置保存文件名": "Установить имя сохраняемого файла",
111
+ "设置文件名: 默认为.json,可选为.md": "Установить имя файла: по умолчанию .json, можно выбрать .md",
112
+ "识别公式": "Распознавание формул",
113
+ "详情": "Подробности",
114
+ "请查看 config_example.json,配置 Azure OpenAI": "Пожалуйста, просмотрите config_example.json для настройки Azure OpenAI",
115
+ "请检查网络连接,或者API-Key是否有效。": "Проверьте подключение к сети или действительность API-Key.",
116
+ "请输入对话内容。": "Пожалуйста, введите содержание диалога.",
117
+ "请输入有效的文件名,不要包含以下特殊字符:": "Введите действительное имя файла, не содержащее следующих специальных символов: ",
118
+ "读取超时,无法获取对话。": "Тайм-аут чтения, не удалось получить диалог.",
119
+ "账单信息不适用": "Информация о счете не применима",
120
+ "连接超时,无法获取对话。": "Тайм-аут подключения, не удалось получить диалог.",
121
+ "选择LoRA模型": "Выберите модель LoRA",
122
+ "选择Prompt模板集合文件": "Выберите файл с набором шаблонов Prompt",
123
+ "选择回复语言(针对搜索&索引功能)": "Выберите язык ответа (для функций поиска и индексации)",
124
+ "选择数据集": "Выберите набор данных",
125
+ "选择模型": "Выберите модель",
126
+ "重命名该对话": "Переименовать этот диалог",
127
+ "重新生成": "Пересоздать",
128
+ "高级": "Расширенные настройки",
129
+ ",本次对话累计消耗了 ": ", Общая стоимость этого диалога составляет ",
130
+ "💾 保存对话": "💾 Сохранить диалог",
131
+ "📝 导出为 Markdown": "📝 Экспортировать в Markdown",
132
+ "🔄 切换API地址": "🔄 Переключить адрес API",
133
+ "🔄 刷新": "🔄 Обновить",
134
+ "🔄 检查更新...": "🔄 Проверить обновления...",
135
+ "🔄 设置代理地址": "🔄 Установить адрес прокси",
136
+ "🔄 重新生成": "🔄 Пересоздать",
137
+ "🔙 恢复默认网络设置": "🔙 Восстановить настройки сети по умолчанию",
138
+ "🗑️ 删除最新对话": "🗑️ Удалить последний диалог",
139
+ "🗑️ 删除最旧对话": "🗑️ Удалить старейший диалог",
140
+ "🧹 新的对话": "🧹 Новый диалог"
141
+ }
locale/sv_SE.json ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ " 吗?": " ?",
3
+ "# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ Var försiktig med ändringar. ⚠️",
4
+ "**发送消息** 或 **提交key** 以显示额度": "**Skicka meddelande** eller **Skicka in nyckel** för att visa kredit",
5
+ "**本月使用金额** ": "**Månadens användning** ",
6
+ "**获取API使用情况失败**": "**Misslyckades med att hämta API-användning**",
7
+ "**获取API使用情况失败**,sensitive_id错误或已过期": "**Misslyckades med att hämta API-användning**, felaktig eller utgången sensitive_id",
8
+ "**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**Misslyckades med att hämta API-användning**, korrekt sensitive_id behövs i `config.json`",
9
+ "API key为空,请检查是否输入正确。": "API-nyckeln är tom, kontrollera om den är korrekt inmatad.",
10
+ "API密钥更改为了": "API-nyckeln har ändrats till",
11
+ "JSON解析错误,收到的内容: ": "JSON-tolkningsfel, mottaget innehåll: ",
12
+ "SSL错误,无法获取对话。": "SSL-fel, kunde inte hämta dialogen.",
13
+ "Token 计数: ": "Tokenräkning: ",
14
+ "☹️发生了错误:": "☹️Fel: ",
15
+ "⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "⚠️ För att säkerställa säkerheten för API-nyckeln, vänligen ändra nätverksinställningarna i konfigurationsfilen `config.json`.",
16
+ "。你仍然可以使用聊天功能。": ". Du kan fortfarande använda chattfunktionen.",
17
+ "上传": "Ladda upp",
18
+ "上传了": "Uppladdad",
19
+ "上传到 OpenAI 后自动填充": "Automatiskt ifylld efter uppladdning till OpenAI",
20
+ "上传到OpenAI": "Ladda upp till OpenAI",
21
+ "上传文件": "ladda upp fil",
22
+ "仅供查看": "Endast för visning",
23
+ "从Prompt模板中加载": "Ladda från Prompt-mall",
24
+ "从列表中加载对话": "Ladda dialog från lista",
25
+ "代理地址": "Proxyadress",
26
+ "代理错误,无法获取对话。": "Proxyfel, kunde inte hämta dialogen.",
27
+ "你没有权限访问 GPT4,[进一步了解](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)": "Du har inte behörighet att komma åt GPT-4, [läs mer](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)",
28
+ "你没有选择任何对话历史": "Du har inte valt någon konversationshistorik.",
29
+ "你真的要删除 ": "Är du säker på att du vill ta bort ",
30
+ "使用在线搜索": "Använd online-sökning",
31
+ "停止符,用英文逗号隔开...": "Skriv in stopptecken här, separerade med kommatecken...",
32
+ "关于": "om",
33
+ "准备数据集": "Förbered dataset",
34
+ "切换亮暗色主题": "Byt ljus/mörk tema",
35
+ "删除对话历史成功": "Raderade konversationens historik.",
36
+ "删除这轮问答": "Ta bort denna omgång av Q&A",
37
+ "刷新状态": "Uppdatera status",
38
+ "剩余配额不足,[进一步了解](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)": "Återstående kvot är otillräcklig, [läs mer](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/%C3%84mnen)",
39
+ "加载Prompt模板": "Ladda Prompt-mall",
40
+ "单轮对话": "Enkel dialog",
41
+ "历史记录(JSON)": "Historikfil (JSON)",
42
+ "参数": "Parametrar",
43
+ "双栏pdf": "Två-kolumns pdf",
44
+ "取消": "Avbryt",
45
+ "取消所有任务": "Avbryt alla uppgifter",
46
+ "可选,用于区分不同的模型": "Valfritt, används för att särskilja olika modeller",
47
+ "启用的工具:": "Aktiverade verktyg: ",
48
+ "在工具箱中管理知识库文件": "hantera kunskapsbankfiler i verktygslådan",
49
+ "在线搜索": "onlinesökning",
50
+ "在这里输入": "Skriv in här",
51
+ "在这里输入System Prompt...": "Skriv in System Prompt här...",
52
+ "多账号模式已开启,无需输入key,可直接开始对话": "Flerkontoläge är aktiverat, ingen nyckel behövs, du kan starta dialogen direkt",
53
+ "好": "OK",
54
+ "实时传输回答": "Strömmande utdata",
55
+ "对话": "konversation",
56
+ "对话历史": "Dialoghistorik",
57
+ "对话历史记录": "Dialoghistorik",
58
+ "对话命名方式": "Dialognamn",
59
+ "导出为 Markdown": "Exportera som Markdown",
60
+ "川虎Chat": "Chuanhu Chat",
61
+ "川虎Chat 🚀": "Chuanhu Chat 🚀",
62
+ "工具箱": "verktygslåda",
63
+ "已经被删除啦": "Har raderats.",
64
+ "开始实时传输回答……": "Börjar strömma utdata...",
65
+ "开始训练": "Börja träning",
66
+ "微调": "Finjustering",
67
+ "总结": "Sammanfatta",
68
+ "总结完成": "Slutfört sammanfattningen.",
69
+ "您使用的就是最新版!": "Du använder den senaste versionen!",
70
+ "您的IP区域:": "Din IP-region: ",
71
+ "您的IP区域:未知。": "Din IP-region: Okänd.",
72
+ "拓展": "utvidgning",
73
+ "搜索(支持正则)...": "Sök (stöd för reguljära uttryck)...",
74
+ "数据集预览": "Datasetförhandsvisning",
75
+ "文件ID": "Fil-ID",
76
+ "新���话 ": "Ny dialog ",
77
+ "新建对话保留Prompt": "Skapa ny konversation med bevarad Prompt",
78
+ "暂时未知": "Okänd",
79
+ "更新": "Uppdatera",
80
+ "更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "Uppdateringen misslyckades, prova att [uppdatera manuellt](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)",
81
+ "更新成功,请重启本程序": "Uppdaterat framgångsrikt, starta om programmet",
82
+ "未命名对话历史记录": "Onämnd Dialoghistorik",
83
+ "未设置代理...": "Inte inställd proxy...",
84
+ "本月使用金额": "Månadens användning",
85
+ "查看[使用介绍](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35)": "Se [användarguiden](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35) för mer information",
86
+ "根据日期时间": "Enligt datum och tid",
87
+ "模型": "Modell",
88
+ "模型名称后缀": "Modellnamnstillägg",
89
+ "模型自动总结(消耗tokens)": "Modellens automatiska sammanfattning (förbrukar tokens)",
90
+ "模型设置为了:": "Modellen är inställd på: ",
91
+ "正在尝试更新...": "Försöker uppdatera...",
92
+ "添加训练好的模型到模型列表": "Lägg till tränad modell i modellistan",
93
+ "状态": "Status",
94
+ "生成内容总结中……": "Genererar innehållssammanfattning...",
95
+ "用于定位滥用行为": "Används för att lokalisera missbruk",
96
+ "用户名": "Användarnamn",
97
+ "由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "Utvecklad av Bilibili [土川虎虎虎](https://space.bilibili.com/29125536), [明昭MZhao](https://space.bilibili.com/24807452) och [Keldos](https://github.com/Keldos-Li)\n\nLadda ner senaste koden från [GitHub](https://github.com/GaiZhenbiao/ChuanhuChatGPT)",
98
+ "知识库": "kunskapsbank",
99
+ "知识库文件": "kunskapsbankfil",
100
+ "第一条提问": "Första frågan",
101
+ "索引构建完成": "Indexet har blivit byggt färdigt.",
102
+ "网络": "nätverksparametrar",
103
+ "获取API使用情况失败:": "Misslyckades med att hämta API-användning:",
104
+ "获取IP地理位置失败。原因:": "Misslyckades med att hämta IP-plats. Orsak: ",
105
+ "获取对话时发生错误,请查看后台日志": "Ett fel uppstod när dialogen hämtades, kontrollera bakgrundsloggen",
106
+ "训练": "träning",
107
+ "训练状态": "Träningsstatus",
108
+ "训练轮数(Epochs)": "Träningsomgångar (Epochs)",
109
+ "设置": "inställningar",
110
+ "设置保存文件名": "Ställ in sparfilnamn",
111
+ "设置文件名: 默认为.json,可选为.md": "Ställ in filnamn: standard är .json, valfritt är .md",
112
+ "识别公式": "Formel OCR",
113
+ "详情": "Detaljer",
114
+ "请查看 config_example.json,配置 Azure OpenAI": "Vänligen granska config_example.json för att konfigurera Azure OpenAI",
115
+ "请检查网络连接,或者API-Key是否有效。": "Kontrollera nätverksanslutningen eller om API-nyckeln är giltig.",
116
+ "请输入对话内容。": "Ange dialoginnehåll.",
117
+ "请输入有效的文件名,不要包含以下特殊字符:": "Ange ett giltigt filnamn, använd inte följande specialtecken: ",
118
+ "读取超时,无法获取对话。": "Läsningen tog för lång tid, kunde inte hämta dialogen.",
119
+ "账单信息不适用": "Faktureringsinformation är inte tillämplig",
120
+ "连接超时,无法获取对话。": "Anslutningen tog för lång tid, kunde inte hämta dialogen.",
121
+ "选择LoRA模型": "Välj LoRA Modell",
122
+ "选择Prompt模板集合文件": "Välj Prompt-mall Samlingsfil",
123
+ "选择回复语言(针对搜索&索引功能)": "Välj svarspråk (för sök- och indexfunktion)",
124
+ "选择数据集": "Välj dataset",
125
+ "选择模型": "Välj Modell",
126
+ "重命名该对话": "Byt namn på dialogen",
127
+ "重新生成": "Återgenerera",
128
+ "高级": "Avancerat",
129
+ ",本次对话累计消耗了 ": ", Total kostnad för denna dialog är ",
130
+ "💾 保存对话": "💾 Spara Dialog",
131
+ "📝 导出为 Markdown": "📝 Exportera som Markdown",
132
+ "🔄 切换API地址": "🔄 Byt API-adress",
133
+ "🔄 刷新": "🔄 Uppdatera",
134
+ "🔄 检查更新...": "🔄 Sök efter uppdateringar...",
135
+ "🔄 设置代理地址": "🔄 Ställ in Proxyadress",
136
+ "🔄 重新生成": "🔄 Regenerera",
137
+ "🔙 恢复默认网络设置": "🔙 Återställ standardnätverksinställningar+",
138
+ "🗑️ 删除最新对话": "🗑️ Ta bort senaste dialogen",
139
+ "🗑️ 删除最旧对话": "🗑️ Ta bort äldsta dialogen",
140
+ "🧹 新的对话": "🧹 Ny Dialog"
141
+ }
locale/vi_VN.json ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ " 吗?": " ?",
3
+ "# ⚠️ 务必谨慎更改 ⚠️": "# ⚠️ Lưu ý: Thay đổi yêu cầu cẩn thận. ⚠️",
4
+ "**发送消息** 或 **提交key** 以显示额度": "**Gửi tin nhắn** hoặc **Gửi khóa(key)** để hiển thị số dư",
5
+ "**本月使用金额** ": "**Số tiền sử dụng trong tháng** ",
6
+ "**获取API使用情况失败**": "**Lỗi khi lấy thông tin sử dụng API**",
7
+ "**获取API使用情况失败**,sensitive_id错误或已过期": "**Lỗi khi lấy thông tin sử dụng API**, sensitive_id sai hoặc đã hết hạn",
8
+ "**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id": "**Lỗi khi lấy thông tin sử dụng API**, cần điền đúng sensitive_id trong tệp `config.json`",
9
+ "API key为空,请检查是否输入正确。": "Khóa API trống, vui lòng kiểm tra xem đã nhập đúng chưa.",
10
+ "API密钥更改为了": "Khóa API đã được thay đổi thành",
11
+ "JSON解析错误,收到的内容: ": "Lỗi phân tích JSON, nội dung nhận được: ",
12
+ "SSL错误,无法获取对话。": "Lỗi SSL, không thể nhận cuộc trò chuyện.",
13
+ "Token 计数: ": "Số lượng Token: ",
14
+ "☹️发生了错误:": "☹️Lỗi: ",
15
+ "⚠️ 为保证API-Key安全,请在配置文件`config.json`中修改网络设置": "⚠️ Để đảm bảo an toàn cho API-Key, vui lòng chỉnh sửa cài đặt mạng trong tệp cấu hình `config.json`.",
16
+ "。你仍然可以使用聊天功能。": ". Bạn vẫn có thể sử dụng chức năng trò chuyện.",
17
+ "上传": "Tải lên",
18
+ "上传了": "Tải lên thành công.",
19
+ "上传到 OpenAI 后自动填充": "Tự động điền sau khi tải lên OpenAI",
20
+ "上传到OpenAI": "Tải lên OpenAI",
21
+ "上传文件": "Tải lên tệp",
22
+ "仅供查看": "Chỉ xem",
23
+ "从Prompt模板中加载": "Tải từ mẫu Prompt",
24
+ "从列表中加载对话": "Tải cuộc trò chuyện từ danh sách",
25
+ "代理地址": "Địa chỉ proxy",
26
+ "代理错误,无法获取对话。": "Lỗi proxy, không thể nhận cuộc trò chuyện.",
27
+ "你没有权限访问 GPT4,[进一步了解](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)": "Bạn không có quyền truy cập GPT-4, [tìm hiểu thêm](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)",
28
+ "你没有选择任何对话历史": "Bạn chưa chọn bất kỳ lịch sử trò chuyện nào.",
29
+ "你真的要删除 ": "Bạn có chắc chắn muốn xóa ",
30
+ "使用在线搜索": "Sử dụng tìm kiếm trực tuyến",
31
+ "停止符,用英文逗号隔开...": "Nhập dấu dừng, cách nhau bằng dấu phẩy...",
32
+ "关于": "Về",
33
+ "准备数据集": "Chuẩn bị tập dữ liệu",
34
+ "切换亮暗色主题": "Chuyển đổi chủ đề sáng/tối",
35
+ "删除对话历史成功": "Xóa lịch sử cuộc trò chuyện thành công.",
36
+ "删除这轮问答": "Xóa cuộc trò chuyện này",
37
+ "刷新状态": "Làm mới tình trạng",
38
+ "剩余配额不足,[进一步了解](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)": "剩余配额 không đủ, [Nhấn vào đây để biết thêm](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)",
39
+ "加载Prompt模板": "Tải mẫu Prompt",
40
+ "单轮对话": "Cuộc trò chuyện một lượt",
41
+ "历史记录(JSON)": "Tệp lịch sử (JSON)",
42
+ "参数": "Tham số",
43
+ "双栏pdf": "PDF hai cột",
44
+ "取消": "Hủy",
45
+ "取消所有任务": "Hủy tất cả các nhiệm vụ",
46
+ "可选,用于区分不同的模型": "Tùy chọn, sử dụng để phân biệt các mô hình khác nhau",
47
+ "启用的工具:": "Công cụ đã bật: ",
48
+ "在工具箱中管理知识库文件": "Quản lý tệp cơ sở kiến thức trong hộp công cụ",
49
+ "在线搜索": "Tìm kiếm trực tuyến",
50
+ "在这里输入": "Nhập vào đây",
51
+ "在这里输入System Prompt...": "Nhập System Prompt ở đây...",
52
+ "多账号模式已开启,无需输入key,可直接开始对话": "Chế độ nhiều tài khoản đã được bật, không cần nhập key, bạn có thể bắt đầu cuộc trò chuyện trực tiếp",
53
+ "好": "OK",
54
+ "实时传输回答": "Truyền đầu ra trực tiếp",
55
+ "对话": "Cuộc trò chuyện",
56
+ "对话历史": "Lịch sử cuộc trò chuyện",
57
+ "对话历史记录": "Lịch sử Cuộc trò chuyện",
58
+ "对话命名方式": "Phương thức đặt tên lịch sử trò chuyện",
59
+ "导出为 Markdown": "Xuất ra Markdown",
60
+ "川虎Chat": "Chuanhu Chat",
61
+ "川虎Chat 🚀": "Chuanhu Chat 🚀",
62
+ "工具箱": "Hộp công cụ",
63
+ "已经��删除啦": "Đã bị xóa rồi.",
64
+ "开始实时传输回答……": "Bắt đầu truyền đầu ra trực tiếp...",
65
+ "开始训练": "Bắt đầu đào tạo",
66
+ "微调": "Feeling-tuning",
67
+ "总结": "Tóm tắt",
68
+ "总结完成": "Hoàn thành tóm tắt",
69
+ "您使用的就是最新版!": "Bạn đang sử dụng phiên bản mới nhất!",
70
+ "您的IP区域:": "Khu vực IP của bạn: ",
71
+ "您的IP区域:未知。": "Khu vực IP của bạn: Không xác định.",
72
+ "拓展": "Mở rộng",
73
+ "搜索(支持正则)...": "Tìm kiếm (hỗ trợ regex)...",
74
+ "数据集预览": "Xem trước tập dữ liệu",
75
+ "文件ID": "ID Tệp",
76
+ "新对话 ": "Cuộc trò chuyện mới ",
77
+ "新建对话保留Prompt": "Tạo Cuộc trò chuyện mới và giữ Prompt nguyên vẹn",
78
+ "暂时未知": "Tạm thời chưa xác định",
79
+ "更新": "Cập nhật",
80
+ "更新失败,请尝试[手动更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)": "Cập nhật thất bại, vui lòng thử [cập nhật thủ công](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#手动更新)",
81
+ "更新成功,请重启本程序": "Cập nhật thành công, vui lòng khởi động lại chương trình này",
82
+ "未命名对话历史记录": "Lịch sử Cuộc trò chuyện không đặt tên",
83
+ "未设置代理...": "Không có proxy...",
84
+ "本月使用金额": "Số tiền sử dụng trong tháng",
85
+ "查看[使用介绍](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35)": "Xem [hướng dẫn sử dụng](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程#微调-gpt-35) để biết thêm chi tiết",
86
+ "根据日期时间": "Theo ngày và giờ",
87
+ "模型": "Mô hình",
88
+ "模型名称后缀": "Hậu tố Tên Mô hình",
89
+ "模型自动总结(消耗tokens)": "Tự động tóm tắt bằng LLM (Tiêu thụ token)",
90
+ "模型设置为了:": "Mô hình đã được đặt thành: ",
91
+ "正在尝试更新...": "Đang cố gắng cập nhật...",
92
+ "添加训练好的模型到模型列表": "Thêm mô hình đã đào tạo vào danh sách mô hình",
93
+ "状态": "Tình trạng",
94
+ "生成内容总结中……": "Đang tạo tóm tắt nội dung...",
95
+ "用于定位滥用行为": "Sử dụng để xác định hành vi lạm dụng",
96
+ "用户名": "Tên người dùng",
97
+ "由Bilibili [土川虎虎虎](https://space.bilibili.com/29125536)、[明昭MZhao](https://space.bilibili.com/24807452) 和 [Keldos](https://github.com/Keldos-Li) 开发<br />访问川虎Chat的 [GitHub项目](https://github.com/GaiZhenbiao/ChuanhuChatGPT) 下载最新版脚本": "Phát triển bởi Bilibili [土川虎虎虎](https://space.bilibili.com/29125536), [明昭MZhao](https://space.bilibili.com/24807452) và [Keldos](https://github.com/Keldos-Li)\n\nTải mã nguồn mới nhất từ [GitHub](https://github.com/GaiZhenbiao/ChuanhuChatGPT)",
98
+ "知识库": "Cơ sở kiến thức",
99
+ "知识库文件": "Tệp cơ sở kiến thức",
100
+ "第一条提问": "Theo câu hỏi đầu tiên",
101
+ "索引构建完成": "Xây dựng chỉ mục hoàn tất",
102
+ "网络": "Mạng",
103
+ "获取API使用情况失败:": "Lỗi khi lấy thông tin sử dụng API:",
104
+ "获取IP地理位置失败。原因:": "Không thể lấy vị trí địa lý của IP. Nguyên nhân: ",
105
+ "获取对话时发生错误,请查看后台日志": "Xảy ra lỗi khi nhận cuộc trò chuyện, kiểm tra nhật ký nền",
106
+ "训练": "Đào tạo",
107
+ "训练状态": "Tình trạng đào tạo",
108
+ "训练轮数(Epochs)": "Số lượt đào tạo (Epochs)",
109
+ "设置": "Cài đặt",
110
+ "设置保存文件名": "Đặt tên tệp lưu",
111
+ "设置文件名: 默认为.json,可选为.md": "Đặt tên tệp: mặc định là .json, tùy chọn là .md",
112
+ "识别公式": "Nhận dạng công thức",
113
+ "详情": "Chi tiết",
114
+ "请查看 config_example.json,配置 Azure OpenAI": "Vui lòng xem tệp config_example.json để cấu hình Azure OpenAI",
115
+ "请检查网络连接,或者API-Key是否有效。": "Vui lòng kiểm tra kết nối mạng hoặc xem xét tính hợp lệ của API-Key.",
116
+ "请输入对话内容。": "Nhập nội dung cuộc trò chuyện.",
117
+ "请输入有效的文件名,不要包含以下特殊字符:": "Vui lòng nhập tên tệp hợp lệ, không chứa các ký tự đặc biệt sau: ",
118
+ "读取超时,无法获取对话。": "Hết thời gian đọc, không thể nhận cuộc trò chuyện.",
119
+ "账单信息不适用": "Thông tin thanh toán không áp dụng",
120
+ "连接超时,无法获取对话。": "Hết thời gian kết nối, không thể nhận cuộc trò chuyện.",
121
+ "选择LoRA模型": "Chọn Mô hình LoRA",
122
+ "选择Prompt模板集合文件": "Chọn Tệp bộ sưu tập mẫu Prompt",
123
+ "选择回复语言��针对搜索&索引功能)": "Chọn ngôn ngữ phản hồi (đối với chức năng tìm kiếm & chỉ mục)",
124
+ "选择数据集": "Chọn tập dữ liệu",
125
+ "选择模型": "Chọn Mô hình",
126
+ "重命名该对话": "Đổi tên cuộc trò chuyện này",
127
+ "重新生成": "Tạo lại",
128
+ "高级": "Nâng cao",
129
+ ",本次对话累计消耗了 ": ", Tổng cộng chi phí cho cuộc trò chuyện này là ",
130
+ "💾 保存对话": "💾 Lưu Cuộc trò chuyện",
131
+ "📝 导出为 Markdown": "📝 Xuất ra dưới dạng Markdown",
132
+ "🔄 切换API地址": "🔄 Chuyển đổi Địa chỉ API",
133
+ "🔄 刷新": "🔄 Làm mới",
134
+ "🔄 检查更新...": "🔄 Kiểm tra cập nhật...",
135
+ "🔄 设置代理地址": "🔄 Đặt Địa chỉ Proxy",
136
+ "🔄 重新生成": "🔄 Tạo lại",
137
+ "🔙 恢复默认网络设置": "🔙 Khôi phục cài đặt mạng mặc định",
138
+ "🗑️ 删除最新对话": "🗑️ Xóa cuộc trò chuyện mới nhất",
139
+ "🗑️ 删除最旧对话": "🗑️ Xóa cuộc trò chuyện cũ nhất",
140
+ "🧹 新的对话": "🧹 Cuộc trò chuyện mới"
141
+ }
locale/zh_CN.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {}
modules/config.py CHANGED
@@ -27,7 +27,8 @@ __all__ = [
27
  "latex_delimiters_set",
28
  "hide_history_when_not_logged_in",
29
  "default_chuanhu_assistant_model",
30
- "show_api_billing"
 
31
  ]
32
 
33
  # 添加一个统一的config文件,避免文件过多造成的疑惑(优先级最低)
@@ -45,15 +46,12 @@ def load_config_to_environ(key_list):
45
  if key in config:
46
  os.environ[key.upper()] = os.environ.get(key.upper(), config[key])
47
 
48
-
49
- lang_config = config.get("language", "auto")
50
- language = os.environ.get("LANGUAGE", lang_config)
51
-
52
  hide_history_when_not_logged_in = config.get(
53
  "hide_history_when_not_logged_in", False)
54
  check_update = config.get("check_update", True)
55
  show_api_billing = config.get("show_api_billing", False)
56
  show_api_billing = bool(os.environ.get("SHOW_API_BILLING", show_api_billing))
 
57
 
58
  if os.path.exists("api_key.txt"):
59
  logging.info("检测到api_key.txt文件,正在进行迁移...")
@@ -96,6 +94,10 @@ else:
96
  sensitive_id = config.get("sensitive_id", "")
97
  sensitive_id = os.environ.get("SENSITIVE_ID", sensitive_id)
98
 
 
 
 
 
99
  # 模型配置
100
  if "extra_models" in config:
101
  presets.MODELS.extend(config["extra_models"])
@@ -123,6 +125,16 @@ os.environ["MIDJOURNEY_DISCORD_PROXY_URL"] = midjourney_discord_proxy_url
123
  midjourney_temp_folder = config.get("midjourney_temp_folder", "")
124
  os.environ["MIDJOURNEY_TEMP_FOLDER"] = midjourney_temp_folder
125
 
 
 
 
 
 
 
 
 
 
 
126
  load_config_to_environ(["openai_api_type", "azure_openai_api_key", "azure_openai_api_base_url",
127
  "azure_openai_api_version", "azure_deployment_name", "azure_embedding_deployment_name", "azure_embedding_model_name"])
128
 
@@ -277,4 +289,12 @@ share = config.get("share", False)
277
 
278
  # avatar
279
  bot_avatar = config.get("bot_avatar", "default")
280
- user_avatar = config.get("user_avatar", "default")
 
 
 
 
 
 
 
 
 
27
  "latex_delimiters_set",
28
  "hide_history_when_not_logged_in",
29
  "default_chuanhu_assistant_model",
30
+ "show_api_billing",
31
+ "chat_name_method_index",
32
  ]
33
 
34
  # 添加一个统一的config文件,避免文件过多造成的疑惑(优先级最低)
 
46
  if key in config:
47
  os.environ[key.upper()] = os.environ.get(key.upper(), config[key])
48
 
 
 
 
 
49
  hide_history_when_not_logged_in = config.get(
50
  "hide_history_when_not_logged_in", False)
51
  check_update = config.get("check_update", True)
52
  show_api_billing = config.get("show_api_billing", False)
53
  show_api_billing = bool(os.environ.get("SHOW_API_BILLING", show_api_billing))
54
+ chat_name_method_index = config.get("chat_name_method_index", 2)
55
 
56
  if os.path.exists("api_key.txt"):
57
  logging.info("检测到api_key.txt文件,正在进行迁移...")
 
94
  sensitive_id = config.get("sensitive_id", "")
95
  sensitive_id = os.environ.get("SENSITIVE_ID", sensitive_id)
96
 
97
+ if "available_models" in config:
98
+ presets.MODELS = config["available_models"]
99
+ logging.info(f"已设置可用模型:{config['available_models']}")
100
+
101
  # 模型配置
102
  if "extra_models" in config:
103
  presets.MODELS.extend(config["extra_models"])
 
125
  midjourney_temp_folder = config.get("midjourney_temp_folder", "")
126
  os.environ["MIDJOURNEY_TEMP_FOLDER"] = midjourney_temp_folder
127
 
128
+ spark_api_key = config.get("spark_api_key", "")
129
+ os.environ["SPARK_API_KEY"] = spark_api_key
130
+ spark_appid = config.get("spark_appid", "")
131
+ os.environ["SPARK_APPID"] = spark_appid
132
+ spark_api_secret = config.get("spark_api_secret", "")
133
+ os.environ["SPARK_API_SECRET"] = spark_api_secret
134
+
135
+ claude_api_secret = config.get("claude_api_secret", "")
136
+ os.environ["CLAUDE_API_SECRET"] = claude_api_secret
137
+
138
  load_config_to_environ(["openai_api_type", "azure_openai_api_key", "azure_openai_api_base_url",
139
  "azure_openai_api_version", "azure_deployment_name", "azure_embedding_deployment_name", "azure_embedding_model_name"])
140
 
 
289
 
290
  # avatar
291
  bot_avatar = config.get("bot_avatar", "default")
292
+ user_avatar = config.get("user_avatar", "default")
293
+ if bot_avatar == "" or bot_avatar == "none" or bot_avatar is None:
294
+ bot_avatar = None
295
+ elif bot_avatar == "default":
296
+ bot_avatar = "web_assets/chatbot.png"
297
+ if user_avatar == "" or user_avatar == "none" or user_avatar is None:
298
+ user_avatar = None
299
+ elif user_avatar == "default":
300
+ user_avatar = "web_assets/user.png"
modules/index_func.py CHANGED
@@ -23,6 +23,7 @@ def get_documents(file_src):
23
  filename = os.path.basename(filepath)
24
  file_type = os.path.splitext(filename)[1]
25
  logging.info(f"loading file: {filename}")
 
26
  try:
27
  if file_type == ".pdf":
28
  logging.debug("Loading PDF...")
@@ -72,8 +73,9 @@ def get_documents(file_src):
72
  logging.error(f"Error loading file: {filename}")
73
  traceback.print_exc()
74
 
75
- texts = text_splitter.split_documents(texts)
76
- documents.extend(texts)
 
77
  logging.debug("Documents loaded.")
78
  return documents
79
 
@@ -87,6 +89,7 @@ def construct_index(
87
  chunk_size_limit=600,
88
  embedding_limit=None,
89
  separator=" ",
 
90
  ):
91
  from langchain.chat_models import ChatOpenAI
92
  from langchain.vectorstores import FAISS
@@ -114,7 +117,7 @@ def construct_index(
114
  else:
115
  embeddings = OpenAIEmbeddings(deployment=os.environ["AZURE_EMBEDDING_DEPLOYMENT_NAME"], openai_api_key=os.environ["AZURE_OPENAI_API_KEY"],
116
  model=os.environ["AZURE_EMBEDDING_MODEL_NAME"], openai_api_base=os.environ["AZURE_OPENAI_API_BASE_URL"], openai_api_type="azure")
117
- if os.path.exists(index_path):
118
  logging.info("找到了缓存的索引文件,加载中……")
119
  return FAISS.load_local(index_path, embeddings)
120
  else:
 
23
  filename = os.path.basename(filepath)
24
  file_type = os.path.splitext(filename)[1]
25
  logging.info(f"loading file: {filename}")
26
+ texts = None
27
  try:
28
  if file_type == ".pdf":
29
  logging.debug("Loading PDF...")
 
73
  logging.error(f"Error loading file: {filename}")
74
  traceback.print_exc()
75
 
76
+ if texts is not None:
77
+ texts = text_splitter.split_documents(texts)
78
+ documents.extend(texts)
79
  logging.debug("Documents loaded.")
80
  return documents
81
 
 
89
  chunk_size_limit=600,
90
  embedding_limit=None,
91
  separator=" ",
92
+ load_from_cache_if_possible=True,
93
  ):
94
  from langchain.chat_models import ChatOpenAI
95
  from langchain.vectorstores import FAISS
 
117
  else:
118
  embeddings = OpenAIEmbeddings(deployment=os.environ["AZURE_EMBEDDING_DEPLOYMENT_NAME"], openai_api_key=os.environ["AZURE_OPENAI_API_KEY"],
119
  model=os.environ["AZURE_EMBEDDING_MODEL_NAME"], openai_api_base=os.environ["AZURE_OPENAI_API_BASE_URL"], openai_api_type="azure")
120
+ if os.path.exists(index_path) and load_from_cache_if_possible:
121
  logging.info("找到了缓存的索引文件,加载中……")
122
  return FAISS.load_local(index_path, embeddings)
123
  else:
modules/models/Azure.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain.chat_models import AzureChatOpenAI, ChatOpenAI
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
+ streaming=True
18
+ )
modules/models/ChatGLM.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import os
5
+ import platform
6
+
7
+ import colorama
8
+
9
+ from ..index_func import *
10
+ from ..presets import *
11
+ from ..utils import *
12
+ from .base_model import BaseLLMModel
13
+
14
+
15
+ class ChatGLM_Client(BaseLLMModel):
16
+ def __init__(self, model_name, user_name="") -> None:
17
+ super().__init__(model_name=model_name, user=user_name)
18
+ import torch
19
+ from transformers import AutoModel, AutoTokenizer
20
+ global CHATGLM_TOKENIZER, CHATGLM_MODEL
21
+ if CHATGLM_TOKENIZER is None or CHATGLM_MODEL is None:
22
+ system_name = platform.system()
23
+ model_path = None
24
+ if os.path.exists("models"):
25
+ model_dirs = os.listdir("models")
26
+ if model_name in model_dirs:
27
+ model_path = f"models/{model_name}"
28
+ if model_path is not None:
29
+ model_source = model_path
30
+ else:
31
+ model_source = f"THUDM/{model_name}"
32
+ CHATGLM_TOKENIZER = AutoTokenizer.from_pretrained(
33
+ model_source, trust_remote_code=True
34
+ )
35
+ quantified = False
36
+ if "int4" in model_name:
37
+ quantified = True
38
+ model = AutoModel.from_pretrained(
39
+ model_source, trust_remote_code=True
40
+ )
41
+ if torch.cuda.is_available():
42
+ # run on CUDA
43
+ logging.info("CUDA is available, using CUDA")
44
+ model = model.half().cuda()
45
+ # mps加速还存在一些问题,暂时不使用
46
+ elif system_name == "Darwin" and model_path is not None and not quantified:
47
+ logging.info("Running on macOS, using MPS")
48
+ # running on macOS and model already downloaded
49
+ model = model.half().to("mps")
50
+ else:
51
+ logging.info("GPU is not available, using CPU")
52
+ model = model.float()
53
+ model = model.eval()
54
+ CHATGLM_MODEL = model
55
+
56
+ def _get_glm_style_input(self):
57
+ history = [x["content"] for x in self.history]
58
+ query = history.pop()
59
+ logging.debug(colorama.Fore.YELLOW +
60
+ f"{history}" + colorama.Fore.RESET)
61
+ assert (
62
+ len(history) % 2 == 0
63
+ ), f"History should be even length. current history is: {history}"
64
+ history = [[history[i], history[i + 1]]
65
+ for i in range(0, len(history), 2)]
66
+ return history, query
67
+
68
+ def get_answer_at_once(self):
69
+ history, query = self._get_glm_style_input()
70
+ response, _ = CHATGLM_MODEL.chat(
71
+ CHATGLM_TOKENIZER, query, history=history)
72
+ return response, len(response)
73
+
74
+ def get_answer_stream_iter(self):
75
+ history, query = self._get_glm_style_input()
76
+ for response, history in CHATGLM_MODEL.stream_chat(
77
+ CHATGLM_TOKENIZER,
78
+ query,
79
+ history,
80
+ max_length=self.token_upper_limit,
81
+ top_p=self.top_p,
82
+ temperature=self.temperature,
83
+ ):
84
+ yield response
modules/models/ChuanhuAgent.py CHANGED
@@ -63,7 +63,23 @@ class ChuanhuAgent_Client(BaseLLMModel):
63
  self.index_summary = None
64
  self.index = None
65
  if "Pro" in self.model_name:
66
- self.tools = load_tools(["serpapi", "google-search-results-json", "llm-math", "arxiv", "wikipedia", "wolfram-alpha"], llm=self.llm)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  else:
68
  self.tools = load_tools(["ddg-search", "llm-math", "arxiv", "wikipedia"], llm=self.llm)
69
  self.tools.append(
@@ -96,7 +112,7 @@ class ChuanhuAgent_Client(BaseLLMModel):
96
  def google_search_simple(self, query):
97
  results = []
98
  with DDGS() as ddgs:
99
- ddgs_gen = ddgs.text("notes from a dead house", backend="lite")
100
  for r in islice(ddgs_gen, 10):
101
  results.append({
102
  "title": r["title"],
 
63
  self.index_summary = None
64
  self.index = None
65
  if "Pro" in self.model_name:
66
+ tools_to_enable = ["llm-math", "arxiv", "wikipedia"]
67
+ # if exists GOOGLE_CSE_ID and GOOGLE_API_KEY, enable google-search-results-json
68
+ if os.environ.get("GOOGLE_CSE_ID", None) is not None and os.environ.get("GOOGLE_API_KEY", None) is not None:
69
+ tools_to_enable.append("google-search-results-json")
70
+ else:
71
+ logging.warning("GOOGLE_CSE_ID and/or GOOGLE_API_KEY not found, google-search-results-json is disabled.")
72
+ # if exists WOLFRAM_ALPHA_APPID, enable wolfram-alpha
73
+ if os.environ.get("WOLFRAM_ALPHA_APPID", None) is not None:
74
+ tools_to_enable.append("wolfram-alpha")
75
+ else:
76
+ logging.warning("WOLFRAM_ALPHA_APPID not found, wolfram-alpha is disabled.")
77
+ # if exists SERPAPI_API_KEY, enable serpapi
78
+ if os.environ.get("SERPAPI_API_KEY", None) is not None:
79
+ tools_to_enable.append("serpapi")
80
+ else:
81
+ logging.warning("SERPAPI_API_KEY not found, serpapi is disabled.")
82
+ self.tools = load_tools(tools_to_enable, llm=self.llm)
83
  else:
84
  self.tools = load_tools(["ddg-search", "llm-math", "arxiv", "wikipedia"], llm=self.llm)
85
  self.tools.append(
 
112
  def google_search_simple(self, query):
113
  results = []
114
  with DDGS() as ddgs:
115
+ ddgs_gen = ddgs.text(query, backend="lite")
116
  for r in islice(ddgs_gen, 10):
117
  results.append({
118
  "title": r["title"],
modules/models/Claude.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from anthropic import Anthropic, HUMAN_PROMPT, AI_PROMPT
3
+ from ..presets import *
4
+ from ..utils import *
5
+
6
+ from .base_model import BaseLLMModel
7
+
8
+
9
+ class Claude_Client(BaseLLMModel):
10
+ def __init__(self, model_name, api_secret) -> None:
11
+ super().__init__(model_name=model_name)
12
+ self.api_secret = api_secret
13
+ if None in [self.api_secret]:
14
+ raise Exception("请在配置文件或者环境变量中设置Claude的API Secret")
15
+ self.claude_client = Anthropic(api_key=self.api_secret)
16
+
17
+
18
+ def get_answer_stream_iter(self):
19
+ system_prompt = self.system_prompt
20
+ history = self.history
21
+ if system_prompt is not None:
22
+ history = [construct_system(system_prompt), *history]
23
+
24
+ completion = self.claude_client.completions.create(
25
+ model=self.model_name,
26
+ max_tokens_to_sample=300,
27
+ prompt=f"{HUMAN_PROMPT}{history}{AI_PROMPT}",
28
+ stream=True,
29
+ )
30
+ if completion is not None:
31
+ partial_text = ""
32
+ for chunk in completion:
33
+ partial_text += chunk.completion
34
+ yield partial_text
35
+ else:
36
+ yield STANDARD_ERROR_MSG + GENERAL_ERROR_MSG
37
+
38
+
39
+ def get_answer_at_once(self):
40
+ system_prompt = self.system_prompt
41
+ history = self.history
42
+ if system_prompt is not None:
43
+ history = [construct_system(system_prompt), *history]
44
+
45
+ completion = self.claude_client.completions.create(
46
+ model=self.model_name,
47
+ max_tokens_to_sample=300,
48
+ prompt=f"{HUMAN_PROMPT}{history}{AI_PROMPT}",
49
+ )
50
+ if completion is not None:
51
+ return completion.completion, len(completion.completion)
52
+ else:
53
+ return "获取资源错误", 0
54
+
55
+
modules/models/GooglePaLM.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .base_model import BaseLLMModel
2
+ import google.generativeai as palm
3
+
4
+
5
+ class Google_PaLM_Client(BaseLLMModel):
6
+ def __init__(self, model_name, api_key, user_name="") -> None:
7
+ super().__init__(model_name=model_name, user=user_name)
8
+ self.api_key = api_key
9
+
10
+ def _get_palm_style_input(self):
11
+ new_history = []
12
+ for item in self.history:
13
+ if item["role"] == "user":
14
+ new_history.append({'author': '1', 'content': item["content"]})
15
+ else:
16
+ new_history.append({'author': '0', 'content': item["content"]})
17
+ return new_history
18
+
19
+ def get_answer_at_once(self):
20
+ palm.configure(api_key=self.api_key)
21
+ messages = self._get_palm_style_input()
22
+ response = palm.chat(context=self.system_prompt, messages=messages,
23
+ temperature=self.temperature, top_p=self.top_p)
24
+ if response.last is not None:
25
+ return response.last, len(response.last)
26
+ else:
27
+ reasons = '\n\n'.join(
28
+ reason['reason'].name for reason in response.filters)
29
+ return "由于下面的原因,Google 拒绝返回 PaLM 的回答:\n\n" + reasons, 0
modules/models/LLaMA.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+
6
+ from huggingface_hub import hf_hub_download
7
+ from llama_cpp import Llama
8
+
9
+ from ..index_func import *
10
+ from ..presets import *
11
+ from ..utils import *
12
+ from .base_model import BaseLLMModel
13
+
14
+ SYS_PREFIX = "<<SYS>>\n"
15
+ SYS_POSTFIX = "\n<</SYS>>\n\n"
16
+ INST_PREFIX = "<s>[INST] "
17
+ INST_POSTFIX = " "
18
+ OUTPUT_PREFIX = "[/INST] "
19
+ OUTPUT_POSTFIX = "</s>"
20
+
21
+
22
+ def download(repo_id, filename, retry=10):
23
+ if os.path.exists("./models/downloaded_models.json"):
24
+ with open("./models/downloaded_models.json", "r") as f:
25
+ downloaded_models = json.load(f)
26
+ if repo_id in downloaded_models:
27
+ return downloaded_models[repo_id]["path"]
28
+ else:
29
+ downloaded_models = {}
30
+ while retry > 0:
31
+ try:
32
+ model_path = hf_hub_download(
33
+ repo_id=repo_id,
34
+ filename=filename,
35
+ cache_dir="models",
36
+ resume_download=True,
37
+ )
38
+ downloaded_models[repo_id] = {"path": model_path}
39
+ with open("./models/downloaded_models.json", "w") as f:
40
+ json.dump(downloaded_models, f)
41
+ break
42
+ except:
43
+ print("Error downloading model, retrying...")
44
+ retry -= 1
45
+ if retry == 0:
46
+ raise Exception("Error downloading model, please try again later.")
47
+ return model_path
48
+
49
+
50
+ class LLaMA_Client(BaseLLMModel):
51
+ def __init__(self, model_name, lora_path=None, user_name="") -> None:
52
+ super().__init__(model_name=model_name, user=user_name)
53
+
54
+ self.max_generation_token = 1000
55
+ if model_name in MODEL_METADATA:
56
+ path_to_model = download(
57
+ MODEL_METADATA[model_name]["repo_id"],
58
+ MODEL_METADATA[model_name]["filelist"][0],
59
+ )
60
+ else:
61
+ dir_to_model = os.path.join("models", model_name)
62
+ # look for nay .gguf file in the dir_to_model directory and its subdirectories
63
+ path_to_model = None
64
+ for root, dirs, files in os.walk(dir_to_model):
65
+ for file in files:
66
+ if file.endswith(".gguf"):
67
+ path_to_model = os.path.join(root, file)
68
+ break
69
+ if path_to_model is not None:
70
+ break
71
+ self.system_prompt = ""
72
+
73
+ if lora_path is not None:
74
+ lora_path = os.path.join("lora", lora_path)
75
+ self.model = Llama(model_path=path_to_model, lora_path=lora_path)
76
+ else:
77
+ self.model = Llama(model_path=path_to_model)
78
+
79
+ def _get_llama_style_input(self):
80
+ context = []
81
+ for conv in self.history:
82
+ if conv["role"] == "system":
83
+ context.append(SYS_PREFIX + conv["content"] + SYS_POSTFIX)
84
+ elif conv["role"] == "user":
85
+ context.append(
86
+ INST_PREFIX + conv["content"] + INST_POSTFIX + OUTPUT_PREFIX
87
+ )
88
+ else:
89
+ context.append(conv["content"] + OUTPUT_POSTFIX)
90
+ return "".join(context)
91
+ # for conv in self.history:
92
+ # if conv["role"] == "system":
93
+ # context.append(conv["content"])
94
+ # elif conv["role"] == "user":
95
+ # context.append(
96
+ # conv["content"]
97
+ # )
98
+ # else:
99
+ # context.append(conv["content"])
100
+ # return "\n\n".join(context)+"\n\n"
101
+
102
+ def get_answer_at_once(self):
103
+ context = self._get_llama_style_input()
104
+ response = self.model(
105
+ context,
106
+ max_tokens=self.max_generation_token,
107
+ stop=[],
108
+ echo=False,
109
+ stream=False,
110
+ )
111
+ return response, len(response)
112
+
113
+ def get_answer_stream_iter(self):
114
+ context = self._get_llama_style_input()
115
+ iter = self.model(
116
+ context,
117
+ max_tokens=self.max_generation_token,
118
+ stop=[SYS_PREFIX, SYS_POSTFIX, INST_PREFIX, OUTPUT_PREFIX,OUTPUT_POSTFIX],
119
+ echo=False,
120
+ stream=True,
121
+ )
122
+ partial_text = ""
123
+ for i in iter:
124
+ response = i["choices"][0]["text"]
125
+ partial_text += response
126
+ yield partial_text
modules/models/OpenAI.py ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import logging
5
+ import traceback
6
+
7
+ import colorama
8
+ import requests
9
+
10
+ from .. import shared
11
+ from ..config import retrieve_proxy, sensitive_id, usage_limit
12
+ from ..index_func import *
13
+ from ..presets import *
14
+ from ..utils import *
15
+ from .base_model import BaseLLMModel
16
+
17
+
18
+ class OpenAIClient(BaseLLMModel):
19
+ def __init__(
20
+ self,
21
+ model_name,
22
+ api_key,
23
+ system_prompt=INITIAL_SYSTEM_PROMPT,
24
+ temperature=1.0,
25
+ top_p=1.0,
26
+ user_name=""
27
+ ) -> None:
28
+ super().__init__(
29
+ model_name=MODEL_METADATA[model_name]["model_name"],
30
+ temperature=temperature,
31
+ top_p=top_p,
32
+ system_prompt=system_prompt,
33
+ user=user_name
34
+ )
35
+ self.api_key = api_key
36
+ self.need_api_key = True
37
+ self._refresh_header()
38
+
39
+ def get_answer_stream_iter(self):
40
+ response = self._get_response(stream=True)
41
+ if response is not None:
42
+ iter = self._decode_chat_response(response)
43
+ partial_text = ""
44
+ for i in iter:
45
+ partial_text += i
46
+ yield partial_text
47
+ else:
48
+ yield STANDARD_ERROR_MSG + GENERAL_ERROR_MSG
49
+
50
+ def get_answer_at_once(self):
51
+ response = self._get_response()
52
+ response = json.loads(response.text)
53
+ content = response["choices"][0]["message"]["content"]
54
+ total_token_count = response["usage"]["total_tokens"]
55
+ return content, total_token_count
56
+
57
+ def count_token(self, user_input):
58
+ input_token_count = count_token(construct_user(user_input))
59
+ if self.system_prompt is not None and len(self.all_token_counts) == 0:
60
+ system_prompt_token_count = count_token(
61
+ construct_system(self.system_prompt)
62
+ )
63
+ return input_token_count + system_prompt_token_count
64
+ return input_token_count
65
+
66
+ def billing_info(self):
67
+ try:
68
+ curr_time = datetime.datetime.now()
69
+ last_day_of_month = get_last_day_of_month(
70
+ curr_time).strftime("%Y-%m-%d")
71
+ first_day_of_month = curr_time.replace(day=1).strftime("%Y-%m-%d")
72
+ usage_url = f"{shared.state.usage_api_url}?start_date={first_day_of_month}&end_date={last_day_of_month}"
73
+ try:
74
+ usage_data = self._get_billing_data(usage_url)
75
+ except Exception as e:
76
+ # logging.error(f"获取API使用情况失败: " + str(e))
77
+ if "Invalid authorization header" in str(e):
78
+ return i18n("**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id")
79
+ elif "Incorrect API key provided: sess" in str(e):
80
+ return i18n("**获取API使用情况失败**,sensitive_id错误或已过期")
81
+ return i18n("**获取API使用情况失败**")
82
+ # rounded_usage = "{:.5f}".format(usage_data["total_usage"] / 100)
83
+ rounded_usage = round(usage_data["total_usage"] / 100, 5)
84
+ usage_percent = round(usage_data["total_usage"] / usage_limit, 2)
85
+ from ..webui import get_html
86
+
87
+ # return i18n("**本月使用金额** ") + f"\u3000 ${rounded_usage}"
88
+ return get_html("billing_info.html").format(
89
+ label = i18n("本月使用金额"),
90
+ usage_percent = usage_percent,
91
+ rounded_usage = rounded_usage,
92
+ usage_limit = usage_limit
93
+ )
94
+ except requests.exceptions.ConnectTimeout:
95
+ status_text = (
96
+ STANDARD_ERROR_MSG + CONNECTION_TIMEOUT_MSG + ERROR_RETRIEVE_MSG
97
+ )
98
+ return status_text
99
+ except requests.exceptions.ReadTimeout:
100
+ status_text = STANDARD_ERROR_MSG + READ_TIMEOUT_MSG + ERROR_RETRIEVE_MSG
101
+ return status_text
102
+ except Exception as e:
103
+ import traceback
104
+ traceback.print_exc()
105
+ logging.error(i18n("获取API使用情况失败:") + str(e))
106
+ return STANDARD_ERROR_MSG + ERROR_RETRIEVE_MSG
107
+
108
+ def set_token_upper_limit(self, new_upper_limit):
109
+ pass
110
+
111
+ @shared.state.switching_api_key # 在不开启多账号模式的时候,这个装饰器不会起作用
112
+ def _get_response(self, stream=False):
113
+ openai_api_key = self.api_key
114
+ system_prompt = self.system_prompt
115
+ history = self.history
116
+ logging.debug(colorama.Fore.YELLOW +
117
+ f"{history}" + colorama.Fore.RESET)
118
+ headers = {
119
+ "Content-Type": "application/json",
120
+ "Authorization": f"Bearer {openai_api_key}",
121
+ }
122
+
123
+ if system_prompt is not None:
124
+ history = [construct_system(system_prompt), *history]
125
+
126
+ payload = {
127
+ "model": self.model_name,
128
+ "messages": history,
129
+ "temperature": self.temperature,
130
+ "top_p": self.top_p,
131
+ "n": self.n_choices,
132
+ "stream": stream,
133
+ "presence_penalty": self.presence_penalty,
134
+ "frequency_penalty": self.frequency_penalty,
135
+ }
136
+
137
+ if self.max_generation_token is not None:
138
+ payload["max_tokens"] = self.max_generation_token
139
+ if self.stop_sequence is not None:
140
+ payload["stop"] = self.stop_sequence
141
+ if self.logit_bias is not None:
142
+ payload["logit_bias"] = self.logit_bias
143
+ if self.user_identifier:
144
+ payload["user"] = self.user_identifier
145
+
146
+ if stream:
147
+ timeout = TIMEOUT_STREAMING
148
+ else:
149
+ timeout = TIMEOUT_ALL
150
+
151
+ # 如果有自定义的api-host,使用自定义host发送请求,否则使用默认设置发送请求
152
+ if shared.state.chat_completion_url != CHAT_COMPLETION_URL:
153
+ logging.debug(f"使用自定义API URL: {shared.state.chat_completion_url}")
154
+
155
+ with retrieve_proxy():
156
+ try:
157
+ response = requests.post(
158
+ shared.state.chat_completion_url,
159
+ headers=headers,
160
+ json=payload,
161
+ stream=stream,
162
+ timeout=timeout,
163
+ )
164
+ except:
165
+ traceback.print_exc()
166
+ return None
167
+ return response
168
+
169
+ def _refresh_header(self):
170
+ self.headers = {
171
+ "Content-Type": "application/json",
172
+ "Authorization": f"Bearer {sensitive_id}",
173
+ }
174
+
175
+
176
+ def _get_billing_data(self, billing_url):
177
+ with retrieve_proxy():
178
+ response = requests.get(
179
+ billing_url,
180
+ headers=self.headers,
181
+ timeout=TIMEOUT_ALL,
182
+ )
183
+
184
+ if response.status_code == 200:
185
+ data = response.json()
186
+ return data
187
+ else:
188
+ raise Exception(
189
+ f"API request failed with status code {response.status_code}: {response.text}"
190
+ )
191
+
192
+ def _decode_chat_response(self, response):
193
+ error_msg = ""
194
+ for chunk in response.iter_lines():
195
+ if chunk:
196
+ chunk = chunk.decode()
197
+ chunk_length = len(chunk)
198
+ try:
199
+ chunk = json.loads(chunk[6:])
200
+ except:
201
+ print(i18n("JSON解析错误,收到的内容: ") + f"{chunk}")
202
+ error_msg += chunk
203
+ continue
204
+ try:
205
+ if chunk_length > 6 and "delta" in chunk["choices"][0]:
206
+ if "finish_reason" in chunk["choices"][0]:
207
+ finish_reason = chunk["choices"][0]["finish_reason"]
208
+ else:
209
+ finish_reason = chunk["finish_reason"]
210
+ if finish_reason == "stop":
211
+ break
212
+ try:
213
+ yield chunk["choices"][0]["delta"]["content"]
214
+ except Exception as e:
215
+ # logging.error(f"Error: {e}")
216
+ continue
217
+ except:
218
+ print(f"ERROR: {chunk}")
219
+ continue
220
+ if error_msg and not error_msg=="data: [DONE]":
221
+ raise Exception(error_msg)
222
+
223
+ def set_key(self, new_access_key):
224
+ ret = super().set_key(new_access_key)
225
+ self._refresh_header()
226
+ return ret
227
+
228
+ def _single_query_at_once(self, history, temperature=1.0):
229
+ timeout = TIMEOUT_ALL
230
+ headers = {
231
+ "Content-Type": "application/json",
232
+ "Authorization": f"Bearer {self.api_key}",
233
+ "temperature": f"{temperature}",
234
+ }
235
+ payload = {
236
+ "model": self.model_name,
237
+ "messages": history,
238
+ }
239
+ # 如果有自定义的api-host,使用自定义host发送请求,否则使用默认设置发送请求
240
+ if shared.state.chat_completion_url != CHAT_COMPLETION_URL:
241
+ logging.debug(f"使用自定义API URL: {shared.state.chat_completion_url}")
242
+
243
+ with retrieve_proxy():
244
+ response = requests.post(
245
+ shared.state.chat_completion_url,
246
+ headers=headers,
247
+ json=payload,
248
+ stream=False,
249
+ timeout=timeout,
250
+ )
251
+
252
+ return response
253
+
254
+
255
+ def auto_name_chat_history(self, name_chat_method, user_question, chatbot, user_name, single_turn_checkbox):
256
+ if len(self.history) == 2 and not single_turn_checkbox and not hide_history_when_not_logged_in:
257
+ user_question = self.history[0]["content"]
258
+ if name_chat_method == i18n("模型自动总结(消耗tokens)"):
259
+ ai_answer = self.history[1]["content"]
260
+ try:
261
+ history = [
262
+ { "role": "system", "content": SUMMARY_CHAT_SYSTEM_PROMPT},
263
+ { "role": "user", "content": f"Please write a title based on the following conversation:\n---\nUser: {user_question}\nAssistant: {ai_answer}"}
264
+ ]
265
+ response = self._single_query_at_once(history, temperature=0.0)
266
+ response = json.loads(response.text)
267
+ content = response["choices"][0]["message"]["content"]
268
+ filename = replace_special_symbols(content) + ".json"
269
+ except Exception as e:
270
+ logging.info(f"自动命名失败。{e}")
271
+ filename = replace_special_symbols(user_question)[:16] + ".json"
272
+ return self.rename_chat_history(filename, chatbot, user_name)
273
+ elif name_chat_method == i18n("第一条提问"):
274
+ filename = replace_special_symbols(user_question)[:16] + ".json"
275
+ return self.rename_chat_history(filename, chatbot, user_name)
276
+ else:
277
+ return gr.update()
278
+ else:
279
+ return gr.update()
modules/models/OpenAIInstruct.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import openai
2
+ from .base_model import BaseLLMModel
3
+ from .. import shared
4
+ from ..config import retrieve_proxy
5
+
6
+
7
+ class OpenAI_Instruct_Client(BaseLLMModel):
8
+ def __init__(self, model_name, api_key, user_name="") -> None:
9
+ super().__init__(model_name=model_name, user=user_name)
10
+ self.api_key = api_key
11
+
12
+ def _get_instruct_style_input(self):
13
+ return "\n\n".join([item["content"] for item in self.history])
14
+
15
+ @shared.state.switching_api_key
16
+ def get_answer_at_once(self):
17
+ prompt = self._get_instruct_style_input()
18
+ with retrieve_proxy():
19
+ response = openai.Completion.create(
20
+ api_key=self.api_key,
21
+ api_base=shared.state.openai_api_base,
22
+ model=self.model_name,
23
+ prompt=prompt,
24
+ temperature=self.temperature,
25
+ top_p=self.top_p,
26
+ )
27
+ return response.choices[0].text.strip(), response.usage["total_tokens"]
modules/models/OpenAIVision.py ADDED
@@ -0,0 +1,328 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import logging
5
+ import traceback
6
+ import base64
7
+
8
+ import colorama
9
+ import requests
10
+ from io import BytesIO
11
+ import uuid
12
+
13
+ import requests
14
+ from PIL import Image
15
+
16
+ from .. import shared
17
+ from ..config import retrieve_proxy, sensitive_id, usage_limit
18
+ from ..index_func import *
19
+ from ..presets import *
20
+ from ..utils import *
21
+ from .base_model import BaseLLMModel
22
+
23
+
24
+ class OpenAIVisionClient(BaseLLMModel):
25
+ def __init__(
26
+ self,
27
+ model_name,
28
+ api_key,
29
+ system_prompt=INITIAL_SYSTEM_PROMPT,
30
+ temperature=1.0,
31
+ top_p=1.0,
32
+ user_name=""
33
+ ) -> None:
34
+ super().__init__(
35
+ model_name=MODEL_METADATA[model_name]["model_name"],
36
+ temperature=temperature,
37
+ top_p=top_p,
38
+ system_prompt=system_prompt,
39
+ user=user_name
40
+ )
41
+ self.api_key = api_key
42
+ self.need_api_key = True
43
+ self.max_generation_token = 4096
44
+ self.images = []
45
+ self._refresh_header()
46
+
47
+ def get_answer_stream_iter(self):
48
+ response = self._get_response(stream=True)
49
+ if response is not None:
50
+ iter = self._decode_chat_response(response)
51
+ partial_text = ""
52
+ for i in iter:
53
+ partial_text += i
54
+ yield partial_text
55
+ else:
56
+ yield STANDARD_ERROR_MSG + GENERAL_ERROR_MSG
57
+
58
+ def get_answer_at_once(self):
59
+ response = self._get_response()
60
+ response = json.loads(response.text)
61
+ content = response["choices"][0]["message"]["content"]
62
+ total_token_count = response["usage"]["total_tokens"]
63
+ return content, total_token_count
64
+
65
+ def try_read_image(self, filepath):
66
+ def is_image_file(filepath):
67
+ # 判断文件是否为图片
68
+ valid_image_extensions = [
69
+ ".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tiff"]
70
+ file_extension = os.path.splitext(filepath)[1].lower()
71
+ return file_extension in valid_image_extensions
72
+ def image_to_base64(image_path):
73
+ # 打开并加载图片
74
+ img = Image.open(image_path)
75
+
76
+ # 获取图片的宽度和高度
77
+ width, height = img.size
78
+
79
+ # 计算压缩比例,以确保最长边小于4096像素
80
+ max_dimension = 2048
81
+ scale_ratio = min(max_dimension / width, max_dimension / height)
82
+
83
+ if scale_ratio < 1:
84
+ # 按压缩比例调整图片大小
85
+ new_width = int(width * scale_ratio)
86
+ new_height = int(height * scale_ratio)
87
+ img = img.resize((new_width, new_height), Image.LANCZOS)
88
+
89
+ # 将图片转换为jpg格式的二进制数据
90
+ buffer = BytesIO()
91
+ if img.mode == "RGBA":
92
+ img = img.convert("RGB")
93
+ img.save(buffer, format='JPEG')
94
+ binary_image = buffer.getvalue()
95
+
96
+ # 对二进制数据进行Base64编码
97
+ base64_image = base64.b64encode(binary_image).decode('utf-8')
98
+
99
+ return base64_image
100
+
101
+ if is_image_file(filepath):
102
+ logging.info(f"读取图片文件: {filepath}")
103
+ base64_image = image_to_base64(filepath)
104
+ self.images.append({
105
+ "path": filepath,
106
+ "base64": base64_image,
107
+ })
108
+
109
+ def handle_file_upload(self, files, chatbot, language):
110
+ """if the model accepts multi modal input, implement this function"""
111
+ if files:
112
+ for file in files:
113
+ if file.name:
114
+ self.try_read_image(file.name)
115
+ if self.images is not None:
116
+ chatbot = chatbot + [([image["path"] for image in self.images], None)]
117
+ return None, chatbot, None
118
+
119
+ def prepare_inputs(self, real_inputs, use_websearch, files, reply_language, chatbot):
120
+ fake_inputs = real_inputs
121
+ display_append = ""
122
+ limited_context = False
123
+ return limited_context, fake_inputs, display_append, real_inputs, chatbot
124
+
125
+
126
+ def count_token(self, user_input):
127
+ input_token_count = count_token(construct_user(user_input))
128
+ if self.system_prompt is not None and len(self.all_token_counts) == 0:
129
+ system_prompt_token_count = count_token(
130
+ construct_system(self.system_prompt)
131
+ )
132
+ return input_token_count + system_prompt_token_count
133
+ return input_token_count
134
+
135
+ def billing_info(self):
136
+ try:
137
+ curr_time = datetime.datetime.now()
138
+ last_day_of_month = get_last_day_of_month(
139
+ curr_time).strftime("%Y-%m-%d")
140
+ first_day_of_month = curr_time.replace(day=1).strftime("%Y-%m-%d")
141
+ usage_url = f"{shared.state.usage_api_url}?start_date={first_day_of_month}&end_date={last_day_of_month}"
142
+ try:
143
+ usage_data = self._get_billing_data(usage_url)
144
+ except Exception as e:
145
+ # logging.error(f"获取API使用情况失败: " + str(e))
146
+ if "Invalid authorization header" in str(e):
147
+ return i18n("**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id")
148
+ elif "Incorrect API key provided: sess" in str(e):
149
+ return i18n("**获取API使用情况失败**,sensitive_id错误或已过期")
150
+ return i18n("**获取API使用情况失败**")
151
+ # rounded_usage = "{:.5f}".format(usage_data["total_usage"] / 100)
152
+ rounded_usage = round(usage_data["total_usage"] / 100, 5)
153
+ usage_percent = round(usage_data["total_usage"] / usage_limit, 2)
154
+ from ..webui import get_html
155
+
156
+ # return i18n("**本月使用金额** ") + f"\u3000 ${rounded_usage}"
157
+ return get_html("billing_info.html").format(
158
+ label = i18n("本月使用金额"),
159
+ usage_percent = usage_percent,
160
+ rounded_usage = rounded_usage,
161
+ usage_limit = usage_limit
162
+ )
163
+ except requests.exceptions.ConnectTimeout:
164
+ status_text = (
165
+ STANDARD_ERROR_MSG + CONNECTION_TIMEOUT_MSG + ERROR_RETRIEVE_MSG
166
+ )
167
+ return status_text
168
+ except requests.exceptions.ReadTimeout:
169
+ status_text = STANDARD_ERROR_MSG + READ_TIMEOUT_MSG + ERROR_RETRIEVE_MSG
170
+ return status_text
171
+ except Exception as e:
172
+ import traceback
173
+ traceback.print_exc()
174
+ logging.error(i18n("获取API使用情况失败:") + str(e))
175
+ return STANDARD_ERROR_MSG + ERROR_RETRIEVE_MSG
176
+
177
+ def set_token_upper_limit(self, new_upper_limit):
178
+ pass
179
+
180
+ @shared.state.switching_api_key # 在不开启多账号模式的时候,这个装饰器不会起作用
181
+ def _get_response(self, stream=False):
182
+ openai_api_key = self.api_key
183
+ system_prompt = self.system_prompt
184
+ history = self.history
185
+ if self.images:
186
+ self.history[-1]["content"] = [
187
+ {"type": "text", "text": self.history[-1]["content"]},
188
+ *[{"type": "image_url", "image_url": "data:image/jpeg;base64,"+image["base64"]} for image in self.images]
189
+ ]
190
+ self.images = []
191
+ logging.debug(colorama.Fore.YELLOW +
192
+ f"{history}" + colorama.Fore.RESET)
193
+ headers = {
194
+ "Content-Type": "application/json",
195
+ "Authorization": f"Bearer {openai_api_key}",
196
+ }
197
+
198
+ if system_prompt is not None:
199
+ history = [construct_system(system_prompt), *history]
200
+
201
+ payload = {
202
+ "model": self.model_name,
203
+ "messages": history,
204
+ "temperature": self.temperature,
205
+ "top_p": self.top_p,
206
+ "n": self.n_choices,
207
+ "stream": stream,
208
+ "presence_penalty": self.presence_penalty,
209
+ "frequency_penalty": self.frequency_penalty,
210
+ }
211
+
212
+ if self.max_generation_token is not None:
213
+ payload["max_tokens"] = self.max_generation_token
214
+ if self.stop_sequence is not None:
215
+ payload["stop"] = self.stop_sequence
216
+ if self.logit_bias is not None:
217
+ payload["logit_bias"] = self.logit_bias
218
+ if self.user_identifier:
219
+ payload["user"] = self.user_identifier
220
+
221
+ if stream:
222
+ timeout = TIMEOUT_STREAMING
223
+ else:
224
+ timeout = TIMEOUT_ALL
225
+
226
+ # 如果有自定义的api-host,使用自定义host发送请求,否则使用默认设置发送请求
227
+ if shared.state.chat_completion_url != CHAT_COMPLETION_URL:
228
+ logging.debug(f"使用自定义API URL: {shared.state.chat_completion_url}")
229
+
230
+ with retrieve_proxy():
231
+ try:
232
+ response = requests.post(
233
+ shared.state.chat_completion_url,
234
+ headers=headers,
235
+ json=payload,
236
+ stream=stream,
237
+ timeout=timeout,
238
+ )
239
+ except:
240
+ traceback.print_exc()
241
+ return None
242
+ return response
243
+
244
+ def _refresh_header(self):
245
+ self.headers = {
246
+ "Content-Type": "application/json",
247
+ "Authorization": f"Bearer {sensitive_id}",
248
+ }
249
+
250
+
251
+ def _get_billing_data(self, billing_url):
252
+ with retrieve_proxy():
253
+ response = requests.get(
254
+ billing_url,
255
+ headers=self.headers,
256
+ timeout=TIMEOUT_ALL,
257
+ )
258
+
259
+ if response.status_code == 200:
260
+ data = response.json()
261
+ return data
262
+ else:
263
+ raise Exception(
264
+ f"API request failed with status code {response.status_code}: {response.text}"
265
+ )
266
+
267
+ def _decode_chat_response(self, response):
268
+ error_msg = ""
269
+ for chunk in response.iter_lines():
270
+ if chunk:
271
+ chunk = chunk.decode()
272
+ chunk_length = len(chunk)
273
+ try:
274
+ chunk = json.loads(chunk[6:])
275
+ except:
276
+ print(i18n("JSON解析错误,收到的内容: ") + f"{chunk}")
277
+ error_msg += chunk
278
+ continue
279
+ try:
280
+ if chunk_length > 6 and "delta" in chunk["choices"][0]:
281
+ if "finish_details" in chunk["choices"][0]:
282
+ finish_reason = chunk["choices"][0]["finish_details"]
283
+ else:
284
+ finish_reason = chunk["finish_details"]
285
+ if finish_reason == "stop":
286
+ break
287
+ try:
288
+ yield chunk["choices"][0]["delta"]["content"]
289
+ except Exception as e:
290
+ # logging.error(f"Error: {e}")
291
+ continue
292
+ except:
293
+ traceback.print_exc()
294
+ print(f"ERROR: {chunk}")
295
+ continue
296
+ if error_msg and not error_msg=="data: [DONE]":
297
+ raise Exception(error_msg)
298
+
299
+ def set_key(self, new_access_key):
300
+ ret = super().set_key(new_access_key)
301
+ self._refresh_header()
302
+ return ret
303
+
304
+ def _single_query_at_once(self, history, temperature=1.0):
305
+ timeout = TIMEOUT_ALL
306
+ headers = {
307
+ "Content-Type": "application/json",
308
+ "Authorization": f"Bearer {self.api_key}",
309
+ "temperature": f"{temperature}",
310
+ }
311
+ payload = {
312
+ "model": self.model_name,
313
+ "messages": history,
314
+ }
315
+ # 如果有自定义的api-host,使用自定义host发送请求,否则使用默认设置发送请求
316
+ if shared.state.chat_completion_url != CHAT_COMPLETION_URL:
317
+ logging.debug(f"使用自定义API URL: {shared.state.chat_completion_url}")
318
+
319
+ with retrieve_proxy():
320
+ response = requests.post(
321
+ shared.state.chat_completion_url,
322
+ headers=headers,
323
+ json=payload,
324
+ stream=False,
325
+ timeout=timeout,
326
+ )
327
+
328
+ return response
modules/models/Qwen.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import AutoModelForCausalLM, AutoTokenizer
2
+ from transformers.generation import GenerationConfig
3
+ import logging
4
+ import colorama
5
+ from .base_model import BaseLLMModel
6
+ from ..presets import MODEL_METADATA
7
+
8
+
9
+ class Qwen_Client(BaseLLMModel):
10
+ def __init__(self, model_name, user_name="") -> None:
11
+ super().__init__(model_name=model_name, user=user_name)
12
+ self.tokenizer = AutoTokenizer.from_pretrained(MODEL_METADATA[model_name]["repo_id"], trust_remote_code=True, resume_download=True)
13
+ self.model = AutoModelForCausalLM.from_pretrained(MODEL_METADATA[model_name]["repo_id"], device_map="auto", trust_remote_code=True, resume_download=True).eval()
14
+
15
+ def generation_config(self):
16
+ return GenerationConfig.from_dict({
17
+ "chat_format": "chatml",
18
+ "do_sample": True,
19
+ "eos_token_id": 151643,
20
+ "max_length": self.token_upper_limit,
21
+ "max_new_tokens": 512,
22
+ "max_window_size": 6144,
23
+ "pad_token_id": 151643,
24
+ "top_k": 0,
25
+ "top_p": self.top_p,
26
+ "transformers_version": "4.33.2",
27
+ "trust_remote_code": True,
28
+ "temperature": self.temperature,
29
+ })
30
+
31
+ def _get_glm_style_input(self):
32
+ history = [x["content"] for x in self.history]
33
+ query = history.pop()
34
+ logging.debug(colorama.Fore.YELLOW +
35
+ f"{history}" + colorama.Fore.RESET)
36
+ assert (
37
+ len(history) % 2 == 0
38
+ ), f"History should be even length. current history is: {history}"
39
+ history = [[history[i], history[i + 1]]
40
+ for i in range(0, len(history), 2)]
41
+ return history, query
42
+
43
+ def get_answer_at_once(self):
44
+ history, query = self._get_glm_style_input()
45
+ self.model.generation_config = self.generation_config()
46
+ response, history = self.model.chat(self.tokenizer, query, history=history)
47
+ return response, len(response)
48
+
49
+ def get_answer_stream_iter(self):
50
+ history, query = self._get_glm_style_input()
51
+ self.model.generation_config = self.generation_config()
52
+ for response in self.model.chat_stream(
53
+ self.tokenizer,
54
+ query,
55
+ history,
56
+ ):
57
+ yield response
modules/models/XMChat.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import json
5
+ import logging
6
+ import os
7
+ import uuid
8
+ from io import BytesIO
9
+
10
+ import requests
11
+ from PIL import Image
12
+
13
+ from ..index_func import *
14
+ from ..presets import *
15
+ from ..utils import *
16
+ from .base_model import BaseLLMModel
17
+
18
+
19
+ class XMChat(BaseLLMModel):
20
+ def __init__(self, api_key, user_name=""):
21
+ super().__init__(model_name="xmchat", user=user_name)
22
+ self.api_key = api_key
23
+ self.session_id = None
24
+ self.reset()
25
+ self.image_bytes = None
26
+ self.image_path = None
27
+ self.xm_history = []
28
+ self.url = "https://xmbot.net/web"
29
+ self.last_conv_id = None
30
+
31
+ def reset(self, remain_system_prompt=False):
32
+ self.session_id = str(uuid.uuid4())
33
+ self.last_conv_id = None
34
+ return super().reset()
35
+
36
+ def image_to_base64(self, image_path):
37
+ # 打开并加载图片
38
+ img = Image.open(image_path)
39
+
40
+ # 获取图片的宽度和高度
41
+ width, height = img.size
42
+
43
+ # 计算压缩比例,以确保最长边小于4096像素
44
+ max_dimension = 2048
45
+ scale_ratio = min(max_dimension / width, max_dimension / height)
46
+
47
+ if scale_ratio < 1:
48
+ # 按压缩比例调整图片大小
49
+ new_width = int(width * scale_ratio)
50
+ new_height = int(height * scale_ratio)
51
+ img = img.resize((new_width, new_height), Image.LANCZOS)
52
+
53
+ # 将图片转换为jpg格式的二进制数据
54
+ buffer = BytesIO()
55
+ if img.mode == "RGBA":
56
+ img = img.convert("RGB")
57
+ img.save(buffer, format='JPEG')
58
+ binary_image = buffer.getvalue()
59
+
60
+ # 对二进制数据进行Base64编码
61
+ base64_image = base64.b64encode(binary_image).decode('utf-8')
62
+
63
+ return base64_image
64
+
65
+ def try_read_image(self, filepath):
66
+ def is_image_file(filepath):
67
+ # 判断文件是否为图片
68
+ valid_image_extensions = [
69
+ ".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tiff"]
70
+ file_extension = os.path.splitext(filepath)[1].lower()
71
+ return file_extension in valid_image_extensions
72
+
73
+ if is_image_file(filepath):
74
+ logging.info(f"读取图片文件: {filepath}")
75
+ self.image_bytes = self.image_to_base64(filepath)
76
+ self.image_path = filepath
77
+ else:
78
+ self.image_bytes = None
79
+ self.image_path = None
80
+
81
+ def like(self):
82
+ if self.last_conv_id is None:
83
+ return "点赞失败,你还没发送过消息"
84
+ data = {
85
+ "uuid": self.last_conv_id,
86
+ "appraise": "good"
87
+ }
88
+ requests.post(self.url, json=data)
89
+ return "👍点赞成功,感谢反馈~"
90
+
91
+ def dislike(self):
92
+ if self.last_conv_id is None:
93
+ return "点踩失败,你还没发送过消息"
94
+ data = {
95
+ "uuid": self.last_conv_id,
96
+ "appraise": "bad"
97
+ }
98
+ requests.post(self.url, json=data)
99
+ return "👎点踩成功,感谢反馈~"
100
+
101
+ def prepare_inputs(self, real_inputs, use_websearch, files, reply_language, chatbot):
102
+ fake_inputs = real_inputs
103
+ display_append = ""
104
+ limited_context = False
105
+ return limited_context, fake_inputs, display_append, real_inputs, chatbot
106
+
107
+ def handle_file_upload(self, files, chatbot, language):
108
+ """if the model accepts multi modal input, implement this function"""
109
+ if files:
110
+ for file in files:
111
+ if file.name:
112
+ logging.info(f"尝试读取图像: {file.name}")
113
+ self.try_read_image(file.name)
114
+ if self.image_path is not None:
115
+ chatbot = chatbot + [((self.image_path,), None)]
116
+ if self.image_bytes is not None:
117
+ logging.info("使用图片作为输入")
118
+ # XMChat的一轮对话中实际上只能处理一张图片
119
+ self.reset()
120
+ conv_id = str(uuid.uuid4())
121
+ data = {
122
+ "user_id": self.api_key,
123
+ "session_id": self.session_id,
124
+ "uuid": conv_id,
125
+ "data_type": "imgbase64",
126
+ "data": self.image_bytes
127
+ }
128
+ response = requests.post(self.url, json=data)
129
+ response = json.loads(response.text)
130
+ logging.info(f"图片回复: {response['data']}")
131
+ return None, chatbot, None
132
+
133
+ def get_answer_at_once(self):
134
+ question = self.history[-1]["content"]
135
+ conv_id = str(uuid.uuid4())
136
+ self.last_conv_id = conv_id
137
+ data = {
138
+ "user_id": self.api_key,
139
+ "session_id": self.session_id,
140
+ "uuid": conv_id,
141
+ "data_type": "text",
142
+ "data": question
143
+ }
144
+ response = requests.post(self.url, json=data)
145
+ try:
146
+ response = json.loads(response.text)
147
+ return response["data"], len(response["data"])
148
+ except Exception as e:
149
+ return response.text, len(response.text)
modules/models/base_model.py CHANGED
@@ -10,6 +10,7 @@ import requests
10
  import urllib3
11
  import traceback
12
  import pathlib
 
13
 
14
  from tqdm import tqdm
15
  import colorama
@@ -142,13 +143,23 @@ class ModelType(Enum):
142
  GooglePaLM = 9
143
  LangchainChat = 10
144
  Midjourney = 11
 
 
 
 
 
145
 
146
  @classmethod
147
  def get_type(cls, model_name: str):
148
  model_type = None
149
  model_name_lower = model_name.lower()
150
  if "gpt" in model_name_lower:
151
- model_type = ModelType.OpenAI
 
 
 
 
 
152
  elif "chatglm" in model_name_lower:
153
  model_type = ModelType.ChatGLM
154
  elif "llama" in model_name_lower or "alpaca" in model_name_lower:
@@ -171,8 +182,14 @@ class ModelType(Enum):
171
  model_type = ModelType.Midjourney
172
  elif "azure" in model_name_lower or "api" in model_name_lower:
173
  model_type = ModelType.LangchainChat
 
 
 
 
 
 
174
  else:
175
- model_type = ModelType.Unknown
176
  return model_type
177
 
178
 
@@ -196,7 +213,7 @@ class BaseLLMModel:
196
  self.model_name = model_name
197
  self.model_type = ModelType.get_type(model_name)
198
  try:
199
- self.token_upper_limit = MODEL_TOKEN_LIMIT[model_name]
200
  except KeyError:
201
  self.token_upper_limit = DEFAULT_TOKEN_LIMIT
202
  self.interrupted = False
@@ -204,6 +221,7 @@ class BaseLLMModel:
204
  self.api_key = None
205
  self.need_api_key = False
206
  self.single_turn = False
 
207
 
208
  self.temperature = temperature
209
  self.top_p = top_p
@@ -242,7 +260,7 @@ class BaseLLMModel:
242
 
243
  def billing_info(self):
244
  """get billing infomation, inplement if needed"""
245
- logging.warning("billing info not implemented, using default")
246
  return BILLING_NOT_APPLICABLE_MSG
247
 
248
  def count_token(self, user_input):
@@ -269,9 +287,12 @@ class BaseLLMModel:
269
  if display_append:
270
  display_append = '\n\n<hr class="append-display no-in-raw" />' + display_append
271
  partial_text = ""
 
272
  for partial_text in stream_iter:
 
 
273
  chatbot[-1] = (chatbot[-1][0], partial_text + display_append)
274
- self.all_token_counts[-1] += 1
275
  status_text = self.token_message()
276
  yield get_return_value()
277
  if self.interrupted:
@@ -334,41 +355,55 @@ class BaseLLMModel:
334
  chatbot.append([i18n("上传了")+str(len(files))+"个文件", summary])
335
  return chatbot, status
336
 
337
- def prepare_inputs(self, real_inputs, use_websearch, files, reply_language, chatbot):
338
- fake_inputs = None
339
  display_append = []
340
  limited_context = False
341
- fake_inputs = real_inputs
 
 
 
342
  if files:
343
  from langchain.embeddings.huggingface import HuggingFaceEmbeddings
344
  from langchain.vectorstores.base import VectorStoreRetriever
345
  limited_context = True
346
  msg = "加载索引中……"
347
  logging.info(msg)
348
- index = construct_index(self.api_key, file_src=files)
349
  assert index is not None, "获取索引失败"
350
  msg = "索引获取成功,生成回答中……"
351
  logging.info(msg)
352
  with retrieve_proxy():
353
- retriever = VectorStoreRetriever(vectorstore=index, search_type="similarity_score_threshold", search_kwargs={
354
- "k": 6, "score_threshold": 0.5})
355
- relevant_documents = retriever.get_relevant_documents(
356
- real_inputs)
 
 
 
 
357
  reference_results = [[d.page_content.strip("�"), os.path.basename(
358
  d.metadata["source"])] for d in relevant_documents]
359
  reference_results = add_source_numbers(reference_results)
360
  display_append = add_details(reference_results)
361
  display_append = "\n\n" + "".join(display_append)
362
- real_inputs = (
363
- replace_today(PROMPT_TEMPLATE)
364
- .replace("{query_str}", real_inputs)
365
- .replace("{context_str}", "\n\n".join(reference_results))
366
- .replace("{reply_language}", reply_language)
367
- )
 
 
 
 
 
 
 
 
368
  elif use_websearch:
369
  search_results = []
370
  with DDGS() as ddgs:
371
- ddgs_gen = ddgs.text(real_inputs, backend="lite")
372
  for r in islice(ddgs_gen, 10):
373
  search_results.append(r)
374
  reference_results = []
@@ -384,12 +419,20 @@ class BaseLLMModel:
384
  # display_append = "<ol>\n\n" + "".join(display_append) + "</ol>"
385
  display_append = '<div class = "source-a">' + \
386
  "".join(display_append) + '</div>'
387
- real_inputs = (
388
- replace_today(WEBSEARCH_PTOMPT_TEMPLATE)
389
- .replace("{query}", real_inputs)
390
- .replace("{web_results}", "\n\n".join(reference_results))
391
- .replace("{reply_language}", reply_language)
392
- )
 
 
 
 
 
 
 
 
393
  else:
394
  display_append = ""
395
  return limited_context, fake_inputs, display_append, real_inputs, chatbot
@@ -406,12 +449,21 @@ class BaseLLMModel:
406
  ): # repetition_penalty, top_k
407
 
408
  status_text = "开始生成回答……"
409
- logging.info(
410
- "用户" + f"{self.user_identifier}" + "的输入为:" +
411
- colorama.Fore.BLUE + f"{inputs}" + colorama.Style.RESET_ALL
412
- )
 
 
 
 
 
 
413
  if should_check_token_count:
414
- yield chatbot + [(inputs, "")], status_text
 
 
 
415
  if reply_language == "跟随问题语言(不稳定)":
416
  reply_language = "the same language as the question, such as English, 中文, 日本語, Español, Français, or Deutsch."
417
 
@@ -426,25 +478,28 @@ class BaseLLMModel:
426
  ):
427
  status_text = STANDARD_ERROR_MSG + NO_APIKEY_MSG
428
  logging.info(status_text)
429
- chatbot.append((inputs, ""))
430
  if len(self.history) == 0:
431
- self.history.append(construct_user(inputs))
432
  self.history.append("")
433
  self.all_token_counts.append(0)
434
  else:
435
- self.history[-2] = construct_user(inputs)
436
- yield chatbot + [(inputs, "")], status_text
437
  return
438
- elif len(inputs.strip()) == 0:
439
  status_text = STANDARD_ERROR_MSG + NO_INPUT_MSG
440
  logging.info(status_text)
441
- yield chatbot + [(inputs, "")], status_text
442
  return
443
 
444
  if self.single_turn:
445
  self.history = []
446
  self.all_token_counts = []
447
- self.history.append(construct_user(inputs))
 
 
 
448
 
449
  try:
450
  if stream:
@@ -471,7 +526,7 @@ class BaseLLMModel:
471
  status_text = STANDARD_ERROR_MSG + beautify_err_msg(str(e))
472
  yield chatbot, status_text
473
 
474
- if len(self.history) > 1 and self.history[-1]["content"] != inputs:
475
  logging.info(
476
  "回答为:"
477
  + colorama.Fore.BLUE
@@ -512,13 +567,19 @@ class BaseLLMModel:
512
  reply_language="中文",
513
  ):
514
  logging.debug("重试中……")
515
- if len(self.history) > 0:
516
  inputs = self.history[-2]["content"]
517
  del self.history[-2:]
518
- if len(self.all_token_counts) > 0:
519
- self.all_token_counts.pop()
520
  elif len(chatbot) > 0:
521
  inputs = chatbot[-1][0]
 
 
 
 
 
 
522
  else:
523
  yield chatbot, f"{STANDARD_ERROR_MSG}上下文是空的"
524
  return
@@ -613,13 +674,15 @@ class BaseLLMModel:
613
  def set_single_turn(self, new_single_turn):
614
  self.single_turn = new_single_turn
615
 
616
- def reset(self):
617
  self.history = []
618
  self.all_token_counts = []
619
  self.interrupted = False
620
- pathlib.Path(os.path.join(HISTORY_DIR, self.user_identifier, new_auto_history_filename(
621
- os.path.join(HISTORY_DIR, self.user_identifier)))).touch()
622
- return [], self.token_message([0])
 
 
623
 
624
  def delete_first_conversation(self):
625
  if self.history:
@@ -630,18 +693,18 @@ class BaseLLMModel:
630
  def delete_last_conversation(self, chatbot):
631
  if len(chatbot) > 0 and STANDARD_ERROR_MSG in chatbot[-1][1]:
632
  msg = "由于包含报错信息,只删除chatbot记录"
633
- chatbot.pop()
634
  return chatbot, self.history
635
  if len(self.history) > 0:
636
- self.history.pop()
637
- self.history.pop()
638
  if len(chatbot) > 0:
639
  msg = "删除了一组chatbot对话"
640
- chatbot.pop()
641
  if len(self.all_token_counts) > 0:
642
  msg = "删除了一组对话的token计数记录"
643
  self.all_token_counts.pop()
644
  msg = "删除了一组对话"
 
645
  return chatbot, msg
646
 
647
  def token_message(self, token_lst=None):
@@ -652,16 +715,36 @@ class BaseLLMModel:
652
  token_sum += sum(token_lst[: i + 1])
653
  return i18n("Token 计数: ") + f"{sum(token_lst)}" + i18n(",本次对话累计消耗了 ") + f"{token_sum} tokens"
654
 
655
- def save_chat_history(self, filename, chatbot, user_name):
656
  if filename == "":
657
- return
658
  if not filename.endswith(".json"):
659
  filename += ".json"
660
- return save_file(filename, self.system_prompt, self.history, chatbot, user_name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
661
 
662
  def auto_save(self, chatbot):
663
- history_file_path = get_history_filepath(self.user_identifier)
664
- save_file(history_file_path, self.system_prompt,
665
  self.history, chatbot, self.user_identifier)
666
 
667
  def export_markdown(self, filename, chatbot, user_name):
@@ -669,19 +752,27 @@ class BaseLLMModel:
669
  return
670
  if not filename.endswith(".md"):
671
  filename += ".md"
672
- return save_file(filename, self.system_prompt, self.history, chatbot, user_name)
673
-
674
- def load_chat_history(self, filename, user_name):
675
- logging.debug(f"{user_name} 加载对话历史中……")
676
- logging.info(f"filename: {filename}")
677
- if type(filename) != str and filename is not None:
678
- filename = filename.name
 
 
 
 
 
 
679
  try:
680
- if "/" not in filename:
681
  history_file_path = os.path.join(
682
- HISTORY_DIR, user_name, filename)
683
  else:
684
- history_file_path = filename
 
 
685
  with open(history_file_path, "r", encoding="utf-8") as f:
686
  json_s = json.load(f)
687
  try:
@@ -697,13 +788,17 @@ class BaseLLMModel:
697
  logging.info(new_history)
698
  except:
699
  pass
700
- logging.debug(f"{user_name} 加载对话历史完毕")
 
 
 
 
701
  self.history = json_s["history"]
702
- return os.path.basename(filename), json_s["system"], json_s["chatbot"]
703
  except:
704
  # 没有对话历史或者对话历史解析失败
705
- logging.info(f"没有找到对话历史记录 {filename}")
706
- return gr.update(), self.system_prompt, gr.update()
707
 
708
  def delete_chat_history(self, filename, user_name):
709
  if filename == "CANCELED":
@@ -712,25 +807,29 @@ class BaseLLMModel:
712
  return i18n("你没有选择任何对话历史"), gr.update(), gr.update()
713
  if not filename.endswith(".json"):
714
  filename += ".json"
715
- if "/" not in filename:
716
  history_file_path = os.path.join(HISTORY_DIR, user_name, filename)
717
  else:
718
  history_file_path = filename
 
719
  try:
720
  os.remove(history_file_path)
721
- return i18n("删除对话历史成功"), get_history_names(False, user_name), []
 
722
  except:
723
  logging.info(f"删除对话历史失败 {history_file_path}")
724
- return i18n("对话历史")+filename+i18n("已经被删除啦"), gr.update(), gr.update()
725
 
726
  def auto_load(self):
727
- if self.user_identifier == "":
728
- self.reset()
729
- return self.system_prompt, gr.update()
730
- history_file_path = get_history_filepath(self.user_identifier)
731
- filename, system_prompt, chatbot = self.load_chat_history(
732
- history_file_path, self.user_identifier)
733
- return system_prompt, chatbot
 
 
734
 
735
  def like(self):
736
  """like the last response, implement if needed
 
10
  import urllib3
11
  import traceback
12
  import pathlib
13
+ import shutil
14
 
15
  from tqdm import tqdm
16
  import colorama
 
143
  GooglePaLM = 9
144
  LangchainChat = 10
145
  Midjourney = 11
146
+ Spark = 12
147
+ OpenAIInstruct = 13
148
+ Claude = 14
149
+ Qwen = 15
150
+ OpenAIVision = 16
151
 
152
  @classmethod
153
  def get_type(cls, model_name: str):
154
  model_type = None
155
  model_name_lower = model_name.lower()
156
  if "gpt" in model_name_lower:
157
+ if "instruct" in model_name_lower:
158
+ model_type = ModelType.OpenAIInstruct
159
+ elif "vision" in model_name_lower:
160
+ model_type = ModelType.OpenAIVision
161
+ else:
162
+ model_type = ModelType.OpenAI
163
  elif "chatglm" in model_name_lower:
164
  model_type = ModelType.ChatGLM
165
  elif "llama" in model_name_lower or "alpaca" in model_name_lower:
 
182
  model_type = ModelType.Midjourney
183
  elif "azure" in model_name_lower or "api" in model_name_lower:
184
  model_type = ModelType.LangchainChat
185
+ elif "星火大模型" in model_name_lower:
186
+ model_type = ModelType.Spark
187
+ elif "claude" in model_name_lower:
188
+ model_type = ModelType.Claude
189
+ elif "qwen" in model_name_lower:
190
+ model_type = ModelType.Qwen
191
  else:
192
+ model_type = ModelType.LLaMA
193
  return model_type
194
 
195
 
 
213
  self.model_name = model_name
214
  self.model_type = ModelType.get_type(model_name)
215
  try:
216
+ self.token_upper_limit = MODEL_METADATA[model_name]["token_limit"]
217
  except KeyError:
218
  self.token_upper_limit = DEFAULT_TOKEN_LIMIT
219
  self.interrupted = False
 
221
  self.api_key = None
222
  self.need_api_key = False
223
  self.single_turn = False
224
+ self.history_file_path = get_first_history_name(user)
225
 
226
  self.temperature = temperature
227
  self.top_p = top_p
 
260
 
261
  def billing_info(self):
262
  """get billing infomation, inplement if needed"""
263
+ # logging.warning("billing info not implemented, using default")
264
  return BILLING_NOT_APPLICABLE_MSG
265
 
266
  def count_token(self, user_input):
 
287
  if display_append:
288
  display_append = '\n\n<hr class="append-display no-in-raw" />' + display_append
289
  partial_text = ""
290
+ token_increment = 1
291
  for partial_text in stream_iter:
292
+ if type(partial_text) == tuple:
293
+ partial_text, token_increment = partial_text
294
  chatbot[-1] = (chatbot[-1][0], partial_text + display_append)
295
+ self.all_token_counts[-1] += token_increment
296
  status_text = self.token_message()
297
  yield get_return_value()
298
  if self.interrupted:
 
355
  chatbot.append([i18n("上传了")+str(len(files))+"个文件", summary])
356
  return chatbot, status
357
 
358
+ def prepare_inputs(self, real_inputs, use_websearch, files, reply_language, chatbot, load_from_cache_if_possible=True):
 
359
  display_append = []
360
  limited_context = False
361
+ if type(real_inputs) == list:
362
+ fake_inputs = real_inputs[0]['text']
363
+ else:
364
+ fake_inputs = real_inputs
365
  if files:
366
  from langchain.embeddings.huggingface import HuggingFaceEmbeddings
367
  from langchain.vectorstores.base import VectorStoreRetriever
368
  limited_context = True
369
  msg = "加载索引中……"
370
  logging.info(msg)
371
+ index = construct_index(self.api_key, file_src=files, load_from_cache_if_possible=load_from_cache_if_possible)
372
  assert index is not None, "获取索引失败"
373
  msg = "索引获取成功,生成回答中……"
374
  logging.info(msg)
375
  with retrieve_proxy():
376
+ retriever = VectorStoreRetriever(vectorstore=index, search_type="similarity", search_kwargs={"k": 6})
377
+ # retriever = VectorStoreRetriever(vectorstore=index, search_type="similarity_score_threshold", search_kwargs={
378
+ # "k": 6, "score_threshold": 0.2})
379
+ try:
380
+ relevant_documents = retriever.get_relevant_documents(
381
+ fake_inputs)
382
+ except AssertionError:
383
+ return self.prepare_inputs(fake_inputs, use_websearch, files, reply_language, chatbot, load_from_cache_if_possible=False)
384
  reference_results = [[d.page_content.strip("�"), os.path.basename(
385
  d.metadata["source"])] for d in relevant_documents]
386
  reference_results = add_source_numbers(reference_results)
387
  display_append = add_details(reference_results)
388
  display_append = "\n\n" + "".join(display_append)
389
+ if type(real_inputs) == list:
390
+ real_inputs[0]["text"] = (
391
+ replace_today(PROMPT_TEMPLATE)
392
+ .replace("{query_str}", fake_inputs)
393
+ .replace("{context_str}", "\n\n".join(reference_results))
394
+ .replace("{reply_language}", reply_language)
395
+ )
396
+ else:
397
+ real_inputs = (
398
+ replace_today(PROMPT_TEMPLATE)
399
+ .replace("{query_str}", real_inputs)
400
+ .replace("{context_str}", "\n\n".join(reference_results))
401
+ .replace("{reply_language}", reply_language)
402
+ )
403
  elif use_websearch:
404
  search_results = []
405
  with DDGS() as ddgs:
406
+ ddgs_gen = ddgs.text(fake_inputs, backend="lite")
407
  for r in islice(ddgs_gen, 10):
408
  search_results.append(r)
409
  reference_results = []
 
419
  # display_append = "<ol>\n\n" + "".join(display_append) + "</ol>"
420
  display_append = '<div class = "source-a">' + \
421
  "".join(display_append) + '</div>'
422
+ if type(real_inputs) == list:
423
+ real_inputs[0]["text"] = (
424
+ replace_today(WEBSEARCH_PTOMPT_TEMPLATE)
425
+ .replace("{query}", fake_inputs)
426
+ .replace("{web_results}", "\n\n".join(reference_results))
427
+ .replace("{reply_language}", reply_language)
428
+ )
429
+ else:
430
+ real_inputs = (
431
+ replace_today(WEBSEARCH_PTOMPT_TEMPLATE)
432
+ .replace("{query}", fake_inputs)
433
+ .replace("{web_results}", "\n\n".join(reference_results))
434
+ .replace("{reply_language}", reply_language)
435
+ )
436
  else:
437
  display_append = ""
438
  return limited_context, fake_inputs, display_append, real_inputs, chatbot
 
449
  ): # repetition_penalty, top_k
450
 
451
  status_text = "开始生成回答……"
452
+ if type(inputs) == list:
453
+ logging.info(
454
+ "用户" + f"{self.user_identifier}" + "的输入为:" +
455
+ colorama.Fore.BLUE + "(" + str(len(inputs)-1) + " images) " + f"{inputs[0]['text']}" + colorama.Style.RESET_ALL
456
+ )
457
+ else:
458
+ logging.info(
459
+ "用户" + f"{self.user_identifier}" + "的输入为:" +
460
+ colorama.Fore.BLUE + f"{inputs}" + colorama.Style.RESET_ALL
461
+ )
462
  if should_check_token_count:
463
+ if type(inputs) == list:
464
+ yield chatbot + [(inputs[0]['text'], "")], status_text
465
+ else:
466
+ yield chatbot + [(inputs, "")], status_text
467
  if reply_language == "跟随问题语言(不稳定)":
468
  reply_language = "the same language as the question, such as English, 中文, 日本語, Español, Français, or Deutsch."
469
 
 
478
  ):
479
  status_text = STANDARD_ERROR_MSG + NO_APIKEY_MSG
480
  logging.info(status_text)
481
+ chatbot.append((fake_inputs, ""))
482
  if len(self.history) == 0:
483
+ self.history.append(construct_user(fake_inputs))
484
  self.history.append("")
485
  self.all_token_counts.append(0)
486
  else:
487
+ self.history[-2] = construct_user(fake_inputs)
488
+ yield chatbot + [(fake_inputs, "")], status_text
489
  return
490
+ elif len(fake_inputs.strip()) == 0:
491
  status_text = STANDARD_ERROR_MSG + NO_INPUT_MSG
492
  logging.info(status_text)
493
+ yield chatbot + [(fake_inputs, "")], status_text
494
  return
495
 
496
  if self.single_turn:
497
  self.history = []
498
  self.all_token_counts = []
499
+ if type(inputs) == list:
500
+ self.history.append(inputs)
501
+ else:
502
+ self.history.append(construct_user(inputs))
503
 
504
  try:
505
  if stream:
 
526
  status_text = STANDARD_ERROR_MSG + beautify_err_msg(str(e))
527
  yield chatbot, status_text
528
 
529
+ if len(self.history) > 1 and self.history[-1]["content"] != fake_inputs:
530
  logging.info(
531
  "回答为:"
532
  + colorama.Fore.BLUE
 
567
  reply_language="中文",
568
  ):
569
  logging.debug("重试中……")
570
+ if len(self.history) > 1:
571
  inputs = self.history[-2]["content"]
572
  del self.history[-2:]
573
+ if len(self.all_token_counts) > 0:
574
+ self.all_token_counts.pop()
575
  elif len(chatbot) > 0:
576
  inputs = chatbot[-1][0]
577
+ if '<div class="user-message">' in inputs:
578
+ inputs = inputs.split('<div class="user-message">')[1]
579
+ inputs = inputs.split("</div>")[0]
580
+ elif len(self.history) == 1:
581
+ inputs = self.history[-1]["content"]
582
+ del self.history[-1]
583
  else:
584
  yield chatbot, f"{STANDARD_ERROR_MSG}上下文是空的"
585
  return
 
674
  def set_single_turn(self, new_single_turn):
675
  self.single_turn = new_single_turn
676
 
677
+ def reset(self, remain_system_prompt=False):
678
  self.history = []
679
  self.all_token_counts = []
680
  self.interrupted = False
681
+ self.history_file_path = new_auto_history_filename(self.user_identifier)
682
+ history_name = self.history_file_path[:-5]
683
+ choices = [history_name] + get_history_names(self.user_identifier)
684
+ system_prompt = self.system_prompt if remain_system_prompt else ""
685
+ return [], self.token_message([0]), gr.Radio.update(choices=choices, value=history_name), system_prompt
686
 
687
  def delete_first_conversation(self):
688
  if self.history:
 
693
  def delete_last_conversation(self, chatbot):
694
  if len(chatbot) > 0 and STANDARD_ERROR_MSG in chatbot[-1][1]:
695
  msg = "由于包含报错信息,只删除chatbot记录"
696
+ chatbot = chatbot[:-1]
697
  return chatbot, self.history
698
  if len(self.history) > 0:
699
+ self.history = self.history[:-2]
 
700
  if len(chatbot) > 0:
701
  msg = "删除了一组chatbot对话"
702
+ chatbot = chatbot[:-1]
703
  if len(self.all_token_counts) > 0:
704
  msg = "删除了一组对话的token计数记录"
705
  self.all_token_counts.pop()
706
  msg = "删除了一组对话"
707
+ self.auto_save(chatbot)
708
  return chatbot, msg
709
 
710
  def token_message(self, token_lst=None):
 
715
  token_sum += sum(token_lst[: i + 1])
716
  return i18n("Token 计数: ") + f"{sum(token_lst)}" + i18n(",本次对话累计消耗了 ") + f"{token_sum} tokens"
717
 
718
+ def rename_chat_history(self, filename, chatbot, user_name):
719
  if filename == "":
720
+ return gr.update()
721
  if not filename.endswith(".json"):
722
  filename += ".json"
723
+ self.delete_chat_history(self.history_file_path, user_name)
724
+ # 命名重复检测
725
+ repeat_file_index = 2
726
+ full_path = os.path.join(HISTORY_DIR, user_name, filename)
727
+ while os.path.exists(full_path):
728
+ full_path = os.path.join(HISTORY_DIR, user_name, f"{repeat_file_index}_{filename}")
729
+ repeat_file_index += 1
730
+ filename = os.path.basename(full_path)
731
+
732
+ self.history_file_path = filename
733
+ save_file(filename, self.system_prompt, self.history, chatbot, user_name)
734
+ return init_history_list(user_name)
735
+
736
+ def auto_name_chat_history(self, name_chat_method, user_question, chatbot, user_name, single_turn_checkbox):
737
+ if len(self.history) == 2 and not single_turn_checkbox:
738
+ user_question = self.history[0]["content"]
739
+ if type(user_question) == list:
740
+ user_question = user_question[0]["text"]
741
+ filename = replace_special_symbols(user_question)[:16] + ".json"
742
+ return self.rename_chat_history(filename, chatbot, user_name)
743
+ else:
744
+ return gr.update()
745
 
746
  def auto_save(self, chatbot):
747
+ save_file(self.history_file_path, self.system_prompt,
 
748
  self.history, chatbot, self.user_identifier)
749
 
750
  def export_markdown(self, filename, chatbot, user_name):
 
752
  return
753
  if not filename.endswith(".md"):
754
  filename += ".md"
755
+ save_file(filename, self.system_prompt, self.history, chatbot, user_name)
756
+
757
+ def load_chat_history(self, new_history_file_path=None, username=None):
758
+ logging.debug(f"{self.user_identifier} 加载对话历史中……")
759
+ if new_history_file_path is not None:
760
+ if type(new_history_file_path) != str:
761
+ # copy file from new_history_file_path.name to os.path.join(HISTORY_DIR, self.user_identifier)
762
+ new_history_file_path = new_history_file_path.name
763
+ shutil.copyfile(new_history_file_path, os.path.join(
764
+ HISTORY_DIR, self.user_identifier, os.path.basename(new_history_file_path)))
765
+ self.history_file_path = os.path.basename(new_history_file_path)
766
+ else:
767
+ self.history_file_path = new_history_file_path
768
  try:
769
+ if self.history_file_path == os.path.basename(self.history_file_path):
770
  history_file_path = os.path.join(
771
+ HISTORY_DIR, self.user_identifier, self.history_file_path)
772
  else:
773
+ history_file_path = self.history_file_path
774
+ if not self.history_file_path.endswith(".json"):
775
+ history_file_path += ".json"
776
  with open(history_file_path, "r", encoding="utf-8") as f:
777
  json_s = json.load(f)
778
  try:
 
788
  logging.info(new_history)
789
  except:
790
  pass
791
+ if len(json_s["chatbot"]) < len(json_s["history"])//2:
792
+ logging.info("Trimming corrupted history...")
793
+ json_s["history"] = json_s["history"][-len(json_s["chatbot"]):]
794
+ logging.info(f"Trimmed history: {json_s['history']}")
795
+ logging.debug(f"{self.user_identifier} 加载对话历史完毕")
796
  self.history = json_s["history"]
797
+ return os.path.basename(self.history_file_path), json_s["system"], json_s["chatbot"]
798
  except:
799
  # 没有对话历史或者对话历史解析失败
800
+ logging.info(f"没有找到对话历史记录 {self.history_file_path}")
801
+ return self.history_file_path, "", []
802
 
803
  def delete_chat_history(self, filename, user_name):
804
  if filename == "CANCELED":
 
807
  return i18n("你没有选择任何对话历史"), gr.update(), gr.update()
808
  if not filename.endswith(".json"):
809
  filename += ".json"
810
+ if filename == os.path.basename(filename):
811
  history_file_path = os.path.join(HISTORY_DIR, user_name, filename)
812
  else:
813
  history_file_path = filename
814
+ md_history_file_path = history_file_path[:-5] + ".md"
815
  try:
816
  os.remove(history_file_path)
817
+ os.remove(md_history_file_path)
818
+ return i18n("删除对话历史成功"), get_history_list(user_name), []
819
  except:
820
  logging.info(f"删除对话历史失败 {history_file_path}")
821
+ return i18n("对话历史")+filename+i18n("已经被删除啦"), get_history_list(user_name), []
822
 
823
  def auto_load(self):
824
+ filepath = get_history_filepath(self.user_identifier)
825
+ if not filepath:
826
+ self.history_file_path = new_auto_history_filename(
827
+ self.user_identifier)
828
+ else:
829
+ self.history_file_path = filepath
830
+ filename, system_prompt, chatbot = self.load_chat_history()
831
+ filename = filename[:-5]
832
+ return filename, system_prompt, chatbot
833
 
834
  def like(self):
835
  """like the last response, implement if needed
modules/models/midjourney.py CHANGED
@@ -2,11 +2,10 @@ import base64
2
  import io
3
  import json
4
  import logging
 
5
  import pathlib
6
- import time
7
  import tempfile
8
- import os
9
-
10
  from datetime import datetime
11
 
12
  import requests
@@ -14,7 +13,7 @@ import tiktoken
14
  from PIL import Image
15
 
16
  from modules.config import retrieve_proxy
17
- from modules.models.models import XMChat
18
 
19
  mj_proxy_api_base = os.getenv("MIDJOURNEY_PROXY_API_BASE")
20
  mj_discord_proxy_url = os.getenv("MIDJOURNEY_DISCORD_PROXY_URL")
@@ -218,10 +217,10 @@ class Midjourney_Client(XMChat):
218
  logging.info("使用图片作为输入")
219
  return None, chatbot, None
220
 
221
- def reset(self):
222
  self.image_bytes = None
223
  self.image_path = None
224
- return [], "已重置"
225
 
226
  def get_answer_at_once(self):
227
  content = self.history[-1]['content']
@@ -367,7 +366,7 @@ UPSCALE - 确认后放大图片,第一个数值为需要放大的图片(1~4
367
  请使用SD进行UPSCALE
368
  VARIATION - 图片变体,第一个数值为需要放大的图片(1~4),第二参数为任务ID
369
  /mj VARIATION::1::123456789
370
-
371
  【绘图参数】
372
  所有命令默认会带上参数--v 5.2
373
  其他参数参照 https://docs.midjourney.com/docs/parameter-list
 
2
  import io
3
  import json
4
  import logging
5
+ import os
6
  import pathlib
 
7
  import tempfile
8
+ import time
 
9
  from datetime import datetime
10
 
11
  import requests
 
13
  from PIL import Image
14
 
15
  from modules.config import retrieve_proxy
16
+ from modules.models.XMChat import XMChat
17
 
18
  mj_proxy_api_base = os.getenv("MIDJOURNEY_PROXY_API_BASE")
19
  mj_discord_proxy_url = os.getenv("MIDJOURNEY_DISCORD_PROXY_URL")
 
217
  logging.info("使用图片作为输入")
218
  return None, chatbot, None
219
 
220
+ def reset(self, remain_system_prompt=False):
221
  self.image_bytes = None
222
  self.image_path = None
223
+ return super().reset()
224
 
225
  def get_answer_at_once(self):
226
  content = self.history[-1]['content']
 
366
  请使用SD进行UPSCALE
367
  VARIATION - 图片变体,第一个数值为需要放大的图片(1~4),第二参数为任务ID
368
  /mj VARIATION::1::123456789
369
+
370
  【绘图参数】
371
  所有命令默认会带上参数--v 5.2
372
  其他参数参照 https://docs.midjourney.com/docs/parameter-list
modules/models/models.py CHANGED
@@ -1,546 +1,19 @@
1
  from __future__ import annotations
2
- from typing import TYPE_CHECKING, List
3
 
4
  import logging
5
- import json
6
- import commentjson as cjson
7
  import os
8
- import sys
9
- import requests
10
- import urllib3
11
- import platform
12
- import base64
13
- from io import BytesIO
14
- from PIL import Image
15
 
16
- from tqdm import tqdm
17
  import colorama
18
- import asyncio
19
- import aiohttp
20
- from enum import Enum
21
- import uuid
22
 
23
- from ..presets import *
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
 
31
 
32
- class OpenAIClient(BaseLLMModel):
33
- def __init__(
34
- self,
35
- model_name,
36
- api_key,
37
- system_prompt=INITIAL_SYSTEM_PROMPT,
38
- temperature=1.0,
39
- top_p=1.0,
40
- user_name=""
41
- ) -> None:
42
- super().__init__(
43
- model_name=model_name,
44
- temperature=temperature,
45
- top_p=top_p,
46
- system_prompt=system_prompt,
47
- user=user_name
48
- )
49
- self.api_key = api_key
50
- self.need_api_key = True
51
- self._refresh_header()
52
-
53
- def get_answer_stream_iter(self):
54
- response = self._get_response(stream=True)
55
- if response is not None:
56
- iter = self._decode_chat_response(response)
57
- partial_text = ""
58
- for i in iter:
59
- partial_text += i
60
- yield partial_text
61
- else:
62
- yield STANDARD_ERROR_MSG + GENERAL_ERROR_MSG
63
-
64
- def get_answer_at_once(self):
65
- response = self._get_response()
66
- response = json.loads(response.text)
67
- content = response["choices"][0]["message"]["content"]
68
- total_token_count = response["usage"]["total_tokens"]
69
- return content, total_token_count
70
-
71
- def count_token(self, user_input):
72
- input_token_count = count_token(construct_user(user_input))
73
- if self.system_prompt is not None and len(self.all_token_counts) == 0:
74
- system_prompt_token_count = count_token(
75
- construct_system(self.system_prompt)
76
- )
77
- return input_token_count + system_prompt_token_count
78
- return input_token_count
79
-
80
- def billing_info(self):
81
- try:
82
- curr_time = datetime.datetime.now()
83
- last_day_of_month = get_last_day_of_month(
84
- curr_time).strftime("%Y-%m-%d")
85
- first_day_of_month = curr_time.replace(day=1).strftime("%Y-%m-%d")
86
- usage_url = f"{shared.state.usage_api_url}?start_date={first_day_of_month}&end_date={last_day_of_month}"
87
- try:
88
- usage_data = self._get_billing_data(usage_url)
89
- except Exception as e:
90
- # logging.error(f"获取API使用情况失败: " + str(e))
91
- if "Invalid authorization header" in str(e):
92
- return i18n("**获取API使用情况失败**,需在填写`config.json`中正确填写sensitive_id")
93
- elif "Incorrect API key provided: sess" in str(e):
94
- return i18n("**获取API使用情况失败**,sensitive_id错误或已过期")
95
- return i18n("**获取API使用情况失败**")
96
- # rounded_usage = "{:.5f}".format(usage_data["total_usage"] / 100)
97
- rounded_usage = round(usage_data["total_usage"] / 100, 5)
98
- usage_percent = round(usage_data["total_usage"] / usage_limit, 2)
99
- from ..webui import get_html
100
- # return i18n("**本月使用金额** ") + f"\u3000 ${rounded_usage}"
101
- return get_html("billing_info.html").format(
102
- label = i18n("本月使用金额"),
103
- usage_percent = usage_percent,
104
- rounded_usage = rounded_usage,
105
- usage_limit = usage_limit
106
- )
107
- except requests.exceptions.ConnectTimeout:
108
- status_text = (
109
- STANDARD_ERROR_MSG + CONNECTION_TIMEOUT_MSG + ERROR_RETRIEVE_MSG
110
- )
111
- return status_text
112
- except requests.exceptions.ReadTimeout:
113
- status_text = STANDARD_ERROR_MSG + READ_TIMEOUT_MSG + ERROR_RETRIEVE_MSG
114
- return status_text
115
- except Exception as e:
116
- import traceback
117
- traceback.print_exc()
118
- logging.error(i18n("获取API使用情况失败:") + str(e))
119
- return STANDARD_ERROR_MSG + ERROR_RETRIEVE_MSG
120
-
121
- def set_token_upper_limit(self, new_upper_limit):
122
- pass
123
-
124
- @shared.state.switching_api_key # 在不开启多账号模式的时候,这个装饰器不会起作用
125
- def _get_response(self, stream=False):
126
- openai_api_key = self.api_key
127
- system_prompt = self.system_prompt
128
- history = self.history
129
- logging.debug(colorama.Fore.YELLOW +
130
- f"{history}" + colorama.Fore.RESET)
131
- headers = {
132
- "Content-Type": "application/json",
133
- "Authorization": f"Bearer {openai_api_key}",
134
- }
135
-
136
- if system_prompt is not None:
137
- history = [construct_system(system_prompt), *history]
138
-
139
- payload = {
140
- "model": self.model_name,
141
- "messages": history,
142
- "temperature": self.temperature,
143
- "top_p": self.top_p,
144
- "n": self.n_choices,
145
- "stream": stream,
146
- "presence_penalty": self.presence_penalty,
147
- "frequency_penalty": self.frequency_penalty,
148
- }
149
-
150
- if self.max_generation_token is not None:
151
- payload["max_tokens"] = self.max_generation_token
152
- if self.stop_sequence is not None:
153
- payload["stop"] = self.stop_sequence
154
- if self.logit_bias is not None:
155
- payload["logit_bias"] = self.logit_bias
156
- if self.user_identifier:
157
- payload["user"] = self.user_identifier
158
-
159
- if stream:
160
- timeout = TIMEOUT_STREAMING
161
- else:
162
- timeout = TIMEOUT_ALL
163
-
164
- # 如果有自定义的api-host,使用自定义host发送请求,否则使用默认设置发送请求
165
- if shared.state.completion_url != COMPLETION_URL:
166
- logging.debug(f"使用自定义API URL: {shared.state.completion_url}")
167
-
168
- with retrieve_proxy():
169
- try:
170
- response = requests.post(
171
- shared.state.completion_url,
172
- headers=headers,
173
- json=payload,
174
- stream=stream,
175
- timeout=timeout,
176
- )
177
- except:
178
- return None
179
- return response
180
-
181
- def _refresh_header(self):
182
- self.headers = {
183
- "Content-Type": "application/json",
184
- "Authorization": f"Bearer {sensitive_id}",
185
- }
186
-
187
-
188
- def _get_billing_data(self, billing_url):
189
- with retrieve_proxy():
190
- response = requests.get(
191
- billing_url,
192
- headers=self.headers,
193
- timeout=TIMEOUT_ALL,
194
- )
195
-
196
- if response.status_code == 200:
197
- data = response.json()
198
- return data
199
- else:
200
- raise Exception(
201
- f"API request failed with status code {response.status_code}: {response.text}"
202
- )
203
-
204
- def _decode_chat_response(self, response):
205
- error_msg = ""
206
- for chunk in response.iter_lines():
207
- if chunk:
208
- chunk = chunk.decode()
209
- chunk_length = len(chunk)
210
- try:
211
- chunk = json.loads(chunk[6:])
212
- except:
213
- print(i18n("JSON解析错误,收到的内容: ") + f"{chunk}")
214
- error_msg += chunk
215
- continue
216
- if chunk_length > 6 and "delta" in chunk["choices"][0]:
217
- if chunk["choices"][0]["finish_reason"] == "stop":
218
- break
219
- try:
220
- yield chunk["choices"][0]["delta"]["content"]
221
- except Exception as e:
222
- # logging.error(f"Error: {e}")
223
- continue
224
- if error_msg:
225
- raise Exception(error_msg)
226
-
227
- def set_key(self, new_access_key):
228
- ret = super().set_key(new_access_key)
229
- self._refresh_header()
230
- return ret
231
-
232
-
233
- class ChatGLM_Client(BaseLLMModel):
234
- def __init__(self, model_name, user_name="") -> None:
235
- super().__init__(model_name=model_name, user=user_name)
236
- from transformers import AutoTokenizer, AutoModel
237
- import torch
238
- global CHATGLM_TOKENIZER, CHATGLM_MODEL
239
- if CHATGLM_TOKENIZER is None or CHATGLM_MODEL is None:
240
- system_name = platform.system()
241
- model_path = None
242
- if os.path.exists("models"):
243
- model_dirs = os.listdir("models")
244
- if model_name in model_dirs:
245
- model_path = f"models/{model_name}"
246
- if model_path is not None:
247
- model_source = model_path
248
- else:
249
- model_source = f"THUDM/{model_name}"
250
- CHATGLM_TOKENIZER = AutoTokenizer.from_pretrained(
251
- model_source, trust_remote_code=True
252
- )
253
- quantified = False
254
- if "int4" in model_name:
255
- quantified = True
256
- model = AutoModel.from_pretrained(
257
- model_source, trust_remote_code=True
258
- )
259
- if torch.cuda.is_available():
260
- # run on CUDA
261
- logging.info("CUDA is available, using CUDA")
262
- model = model.half().cuda()
263
- # mps加速还存在一些问题,暂时不使用
264
- elif system_name == "Darwin" and model_path is not None and not quantified:
265
- logging.info("Running on macOS, using MPS")
266
- # running on macOS and model already downloaded
267
- model = model.half().to("mps")
268
- else:
269
- logging.info("GPU is not available, using CPU")
270
- model = model.float()
271
- model = model.eval()
272
- CHATGLM_MODEL = model
273
-
274
- def _get_glm_style_input(self):
275
- history = [x["content"] for x in self.history]
276
- query = history.pop()
277
- logging.debug(colorama.Fore.YELLOW +
278
- f"{history}" + colorama.Fore.RESET)
279
- assert (
280
- len(history) % 2 == 0
281
- ), f"History should be even length. current history is: {history}"
282
- history = [[history[i], history[i + 1]]
283
- for i in range(0, len(history), 2)]
284
- return history, query
285
-
286
- def get_answer_at_once(self):
287
- history, query = self._get_glm_style_input()
288
- response, _ = CHATGLM_MODEL.chat(
289
- CHATGLM_TOKENIZER, query, history=history)
290
- return response, len(response)
291
-
292
- def get_answer_stream_iter(self):
293
- history, query = self._get_glm_style_input()
294
- for response, history in CHATGLM_MODEL.stream_chat(
295
- CHATGLM_TOKENIZER,
296
- query,
297
- history,
298
- max_length=self.token_upper_limit,
299
- top_p=self.top_p,
300
- temperature=self.temperature,
301
- ):
302
- yield response
303
-
304
-
305
- class LLaMA_Client(BaseLLMModel):
306
- def __init__(
307
- self,
308
- model_name,
309
- lora_path=None,
310
- user_name=""
311
- ) -> None:
312
- super().__init__(model_name=model_name, user=user_name)
313
- from lmflow.datasets.dataset import Dataset
314
- from lmflow.pipeline.auto_pipeline import AutoPipeline
315
- from lmflow.models.auto_model import AutoModel
316
- from lmflow.args import ModelArguments, DatasetArguments, InferencerArguments
317
-
318
- self.max_generation_token = 1000
319
- self.end_string = "\n\n"
320
- # We don't need input data
321
- data_args = DatasetArguments(dataset_path=None)
322
- self.dataset = Dataset(data_args)
323
- self.system_prompt = ""
324
-
325
- global LLAMA_MODEL, LLAMA_INFERENCER
326
- if LLAMA_MODEL is None or LLAMA_INFERENCER is None:
327
- model_path = None
328
- if os.path.exists("models"):
329
- model_dirs = os.listdir("models")
330
- if model_name in model_dirs:
331
- model_path = f"models/{model_name}"
332
- if model_path is not None:
333
- model_source = model_path
334
- else:
335
- model_source = f"decapoda-research/{model_name}"
336
- # raise Exception(f"models目录下没有这个模型: {model_name}")
337
- if lora_path is not None:
338
- lora_path = f"lora/{lora_path}"
339
- model_args = ModelArguments(model_name_or_path=model_source, lora_model_path=lora_path, model_type=None, config_overrides=None, config_name=None, tokenizer_name=None, cache_dir=None,
340
- use_fast_tokenizer=True, model_revision='main', use_auth_token=False, torch_dtype=None, use_lora=False, lora_r=8, lora_alpha=32, lora_dropout=0.1, use_ram_optimized_load=True)
341
- pipeline_args = InferencerArguments(
342
- local_rank=0, random_seed=1, deepspeed='configs/ds_config_chatbot.json', mixed_precision='bf16')
343
-
344
- with open(pipeline_args.deepspeed, "r", encoding="utf-8") as f:
345
- ds_config = json.load(f)
346
- LLAMA_MODEL = AutoModel.get_model(
347
- model_args,
348
- tune_strategy="none",
349
- ds_config=ds_config,
350
- )
351
- LLAMA_INFERENCER = AutoPipeline.get_pipeline(
352
- pipeline_name="inferencer",
353
- model_args=model_args,
354
- data_args=data_args,
355
- pipeline_args=pipeline_args,
356
- )
357
-
358
- def _get_llama_style_input(self):
359
- history = []
360
- instruction = ""
361
- if self.system_prompt:
362
- instruction = (f"Instruction: {self.system_prompt}\n")
363
- for x in self.history:
364
- if x["role"] == "user":
365
- history.append(f"{instruction}Input: {x['content']}")
366
- else:
367
- history.append(f"Output: {x['content']}")
368
- context = "\n\n".join(history)
369
- context += "\n\nOutput: "
370
- return context
371
-
372
- def get_answer_at_once(self):
373
- context = self._get_llama_style_input()
374
-
375
- input_dataset = self.dataset.from_dict(
376
- {"type": "text_only", "instances": [{"text": context}]}
377
- )
378
-
379
- output_dataset = LLAMA_INFERENCER.inference(
380
- model=LLAMA_MODEL,
381
- dataset=input_dataset,
382
- max_new_tokens=self.max_generation_token,
383
- temperature=self.temperature,
384
- )
385
-
386
- response = output_dataset.to_dict()["instances"][0]["text"]
387
- return response, len(response)
388
-
389
- def get_answer_stream_iter(self):
390
- context = self._get_llama_style_input()
391
- partial_text = ""
392
- step = 1
393
- for _ in range(0, self.max_generation_token, step):
394
- input_dataset = self.dataset.from_dict(
395
- {"type": "text_only", "instances": [
396
- {"text": context + partial_text}]}
397
- )
398
- output_dataset = LLAMA_INFERENCER.inference(
399
- model=LLAMA_MODEL,
400
- dataset=input_dataset,
401
- max_new_tokens=step,
402
- temperature=self.temperature,
403
- )
404
- response = output_dataset.to_dict()["instances"][0]["text"]
405
- if response == "" or response == self.end_string:
406
- break
407
- partial_text += response
408
- yield partial_text
409
-
410
-
411
- class XMChat(BaseLLMModel):
412
- def __init__(self, api_key, user_name=""):
413
- super().__init__(model_name="xmchat", user=user_name)
414
- self.api_key = api_key
415
- self.session_id = None
416
- self.reset()
417
- self.image_bytes = None
418
- self.image_path = None
419
- self.xm_history = []
420
- self.url = "https://xmbot.net/web"
421
- self.last_conv_id = None
422
-
423
- def reset(self):
424
- self.session_id = str(uuid.uuid4())
425
- self.last_conv_id = None
426
- return [], "已重置"
427
-
428
- def image_to_base64(self, image_path):
429
- # 打开并加载图片
430
- img = Image.open(image_path)
431
-
432
- # 获取图片的宽度和高度
433
- width, height = img.size
434
-
435
- # 计算压缩比例,以确保最长边小于4096像素
436
- max_dimension = 2048
437
- scale_ratio = min(max_dimension / width, max_dimension / height)
438
-
439
- if scale_ratio < 1:
440
- # 按压缩比例调整图片大小
441
- new_width = int(width * scale_ratio)
442
- new_height = int(height * scale_ratio)
443
- img = img.resize((new_width, new_height), Image.ANTIALIAS)
444
-
445
- # 将图片转换为jpg格式的二进制数据
446
- buffer = BytesIO()
447
- if img.mode == "RGBA":
448
- img = img.convert("RGB")
449
- img.save(buffer, format='JPEG')
450
- binary_image = buffer.getvalue()
451
-
452
- # 对二进制数据进行Base64编码
453
- base64_image = base64.b64encode(binary_image).decode('utf-8')
454
-
455
- return base64_image
456
-
457
- def try_read_image(self, filepath):
458
- def is_image_file(filepath):
459
- # 判断文件是否为图片
460
- valid_image_extensions = [
461
- ".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tiff"]
462
- file_extension = os.path.splitext(filepath)[1].lower()
463
- return file_extension in valid_image_extensions
464
-
465
- if is_image_file(filepath):
466
- logging.info(f"读取图片文件: {filepath}")
467
- self.image_bytes = self.image_to_base64(filepath)
468
- self.image_path = filepath
469
- else:
470
- self.image_bytes = None
471
- self.image_path = None
472
-
473
- def like(self):
474
- if self.last_conv_id is None:
475
- return "点赞失败,你还没发送过消息"
476
- data = {
477
- "uuid": self.last_conv_id,
478
- "appraise": "good"
479
- }
480
- requests.post(self.url, json=data)
481
- return "👍点赞成功,感谢反馈~"
482
-
483
- def dislike(self):
484
- if self.last_conv_id is None:
485
- return "点踩失败,你还没发送过消息"
486
- data = {
487
- "uuid": self.last_conv_id,
488
- "appraise": "bad"
489
- }
490
- requests.post(self.url, json=data)
491
- return "👎点踩成功,感谢反馈~"
492
-
493
- def prepare_inputs(self, real_inputs, use_websearch, files, reply_language, chatbot):
494
- fake_inputs = real_inputs
495
- display_append = ""
496
- limited_context = False
497
- return limited_context, fake_inputs, display_append, real_inputs, chatbot
498
-
499
- def handle_file_upload(self, files, chatbot, language):
500
- """if the model accepts multi modal input, implement this function"""
501
- if files:
502
- for file in files:
503
- if file.name:
504
- logging.info(f"尝试读取图像: {file.name}")
505
- self.try_read_image(file.name)
506
- if self.image_path is not None:
507
- chatbot = chatbot + [((self.image_path,), None)]
508
- if self.image_bytes is not None:
509
- logging.info("使用图片作为输入")
510
- # XMChat的一轮对话中实际上只能处理一张图片
511
- self.reset()
512
- conv_id = str(uuid.uuid4())
513
- data = {
514
- "user_id": self.api_key,
515
- "session_id": self.session_id,
516
- "uuid": conv_id,
517
- "data_type": "imgbase64",
518
- "data": self.image_bytes
519
- }
520
- response = requests.post(self.url, json=data)
521
- response = json.loads(response.text)
522
- logging.info(f"图片回复: {response['data']}")
523
- return None, chatbot, None
524
-
525
- def get_answer_at_once(self):
526
- question = self.history[-1]["content"]
527
- conv_id = str(uuid.uuid4())
528
- self.last_conv_id = conv_id
529
- data = {
530
- "user_id": self.api_key,
531
- "session_id": self.session_id,
532
- "uuid": conv_id,
533
- "data_type": "text",
534
- "data": question
535
- }
536
- response = requests.post(self.url, json=data)
537
- try:
538
- response = json.loads(response.text)
539
- return response["data"], len(response["data"])
540
- except Exception as e:
541
- return response.text, len(response.text)
542
-
543
-
544
  def get_model(
545
  model_name,
546
  lora_model_path=None,
@@ -548,21 +21,23 @@ def get_model(
548
  temperature=None,
549
  top_p=None,
550
  system_prompt=None,
551
- user_name=""
 
552
  ) -> BaseLLMModel:
553
  msg = i18n("模型设置为了:") + f" {model_name}"
554
  model_type = ModelType.get_type(model_name)
555
  lora_selector_visibility = False
556
- lora_choices = []
557
  dont_change_lora_selector = False
558
  if model_type != ModelType.OpenAI:
559
  config.local_embedding = True
560
  # del current_model.model
561
- model = None
562
  chatbot = gr.Chatbot.update(label=model_name)
563
  try:
564
  if model_type == ModelType.OpenAI:
565
  logging.info(f"正在加载OpenAI模型: {model_name}")
 
566
  access_key = os.environ.get("OPENAI_API_KEY", access_key)
567
  model = OpenAIClient(
568
  model_name=model_name,
@@ -572,19 +47,31 @@ def get_model(
572
  top_p=top_p,
573
  user_name=user_name,
574
  )
 
 
 
 
 
 
 
 
 
 
 
 
575
  elif model_type == ModelType.ChatGLM:
576
  logging.info(f"正在加载ChatGLM模型: {model_name}")
 
577
  model = ChatGLM_Client(model_name, user_name=user_name)
578
  elif model_type == ModelType.LLaMA and lora_model_path == "":
579
  msg = f"现在请为 {model_name} 选择LoRA模型"
580
  logging.info(msg)
581
  lora_selector_visibility = True
582
  if os.path.isdir("lora"):
583
- lora_choices = get_file_names(
584
- "lora", plain=True, filetypes=[""])
585
- lora_choices = ["No LoRA"] + lora_choices
586
  elif model_type == ModelType.LLaMA and lora_model_path != "":
587
  logging.info(f"正在加载LLaMA模型: {model_name} + {lora_model_path}")
 
588
  dont_change_lora_selector = True
589
  if lora_model_path == "No LoRA":
590
  lora_model_path = None
@@ -594,6 +81,7 @@ def get_model(
594
  model = LLaMA_Client(
595
  model_name, lora_model_path, user_name=user_name)
596
  elif model_type == ModelType.XMChat:
 
597
  if os.environ.get("XMCHAT_API_KEY") != "":
598
  access_key = os.environ.get("XMCHAT_API_KEY")
599
  model = XMChat(api_key=access_key, user_name=user_name)
@@ -605,26 +93,41 @@ def get_model(
605
  model = MOSS_Client(model_name, user_name=user_name)
606
  elif model_type == ModelType.YuanAI:
607
  from .inspurai import Yuan_Client
608
- model = Yuan_Client(model_name, api_key=access_key, user_name=user_name, system_prompt=system_prompt)
 
609
  elif model_type == ModelType.Minimax:
610
  from .minimax import MiniMax_Client
611
  if os.environ.get("MINIMAX_API_KEY") != "":
612
  access_key = os.environ.get("MINIMAX_API_KEY")
613
- model = MiniMax_Client(model_name, api_key=access_key, user_name=user_name, system_prompt=system_prompt)
 
614
  elif model_type == ModelType.ChuanhuAgent:
615
  from .ChuanhuAgent import ChuanhuAgent_Client
616
  model = ChuanhuAgent_Client(model_name, access_key, user_name=user_name)
 
617
  elif model_type == ModelType.GooglePaLM:
618
- from .Google_PaLM import Google_PaLM_Client
619
  access_key = os.environ.get("GOOGLE_PALM_API_KEY", access_key)
620
- model = Google_PaLM_Client(model_name, access_key, user_name=user_name)
 
621
  elif model_type == ModelType.LangchainChat:
622
- from .azure import Azure_OpenAI_Client
623
  model = Azure_OpenAI_Client(model_name, user_name=user_name)
624
  elif model_type == ModelType.Midjourney:
625
  from .midjourney import Midjourney_Client
626
  mj_proxy_api_secret = os.getenv("MIDJOURNEY_PROXY_API_SECRET")
627
- model = Midjourney_Client(model_name, mj_proxy_api_secret, user_name=user_name)
 
 
 
 
 
 
 
 
 
 
 
628
  elif model_type == ModelType.Unknown:
629
  raise ValueError(f"未知模型: {model_name}")
630
  logging.info(msg)
@@ -633,6 +136,9 @@ def get_model(
633
  traceback.print_exc()
634
  msg = f"{STANDARD_ERROR_MSG}: {e}"
635
  presudo_key = hide_middle_chars(access_key)
 
 
 
636
  if dont_change_lora_selector:
637
  return model, msg, chatbot, gr.update(), access_key, presudo_key
638
  else:
 
1
  from __future__ import annotations
 
2
 
3
  import logging
 
 
4
  import os
 
 
 
 
 
 
 
5
 
 
6
  import colorama
7
+ import commentjson as cjson
8
+
9
+ from modules import config
 
10
 
 
11
  from ..index_func import *
12
+ from ..presets import *
13
  from ..utils import *
 
 
 
14
  from .base_model import BaseLLMModel, ModelType
15
 
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  def get_model(
18
  model_name,
19
  lora_model_path=None,
 
21
  temperature=None,
22
  top_p=None,
23
  system_prompt=None,
24
+ user_name="",
25
+ original_model = None
26
  ) -> BaseLLMModel:
27
  msg = i18n("模型设置为了:") + f" {model_name}"
28
  model_type = ModelType.get_type(model_name)
29
  lora_selector_visibility = False
30
+ lora_choices = ["No LoRA"]
31
  dont_change_lora_selector = False
32
  if model_type != ModelType.OpenAI:
33
  config.local_embedding = True
34
  # del current_model.model
35
+ model = original_model
36
  chatbot = gr.Chatbot.update(label=model_name)
37
  try:
38
  if model_type == ModelType.OpenAI:
39
  logging.info(f"正在加载OpenAI模型: {model_name}")
40
+ from .OpenAI import OpenAIClient
41
  access_key = os.environ.get("OPENAI_API_KEY", access_key)
42
  model = OpenAIClient(
43
  model_name=model_name,
 
47
  top_p=top_p,
48
  user_name=user_name,
49
  )
50
+ elif model_type == ModelType.OpenAIInstruct:
51
+ logging.info(f"正在加载OpenAI Instruct模型: {model_name}")
52
+ from .OpenAIInstruct import OpenAI_Instruct_Client
53
+ access_key = os.environ.get("OPENAI_API_KEY", access_key)
54
+ model = OpenAI_Instruct_Client(
55
+ model_name, api_key=access_key, user_name=user_name)
56
+ elif model_type == ModelType.OpenAIVision:
57
+ logging.info(f"正在加载OpenAI Vision模型: {model_name}")
58
+ from .OpenAIVision import OpenAIVisionClient
59
+ access_key = os.environ.get("OPENAI_API_KEY", access_key)
60
+ model = OpenAIVisionClient(
61
+ model_name, api_key=access_key, user_name=user_name)
62
  elif model_type == ModelType.ChatGLM:
63
  logging.info(f"正在加载ChatGLM模型: {model_name}")
64
+ from .ChatGLM import ChatGLM_Client
65
  model = ChatGLM_Client(model_name, user_name=user_name)
66
  elif model_type == ModelType.LLaMA and lora_model_path == "":
67
  msg = f"现在请为 {model_name} 选择LoRA模型"
68
  logging.info(msg)
69
  lora_selector_visibility = True
70
  if os.path.isdir("lora"):
71
+ lora_choices = ["No LoRA"] + get_file_names_by_pinyin("lora", filetypes=[""])
 
 
72
  elif model_type == ModelType.LLaMA and lora_model_path != "":
73
  logging.info(f"正在加载LLaMA模型: {model_name} + {lora_model_path}")
74
+ from .LLaMA import LLaMA_Client
75
  dont_change_lora_selector = True
76
  if lora_model_path == "No LoRA":
77
  lora_model_path = None
 
81
  model = LLaMA_Client(
82
  model_name, lora_model_path, user_name=user_name)
83
  elif model_type == ModelType.XMChat:
84
+ from .XMChat import XMChat
85
  if os.environ.get("XMCHAT_API_KEY") != "":
86
  access_key = os.environ.get("XMCHAT_API_KEY")
87
  model = XMChat(api_key=access_key, user_name=user_name)
 
93
  model = MOSS_Client(model_name, user_name=user_name)
94
  elif model_type == ModelType.YuanAI:
95
  from .inspurai import Yuan_Client
96
+ model = Yuan_Client(model_name, api_key=access_key,
97
+ user_name=user_name, system_prompt=system_prompt)
98
  elif model_type == ModelType.Minimax:
99
  from .minimax import MiniMax_Client
100
  if os.environ.get("MINIMAX_API_KEY") != "":
101
  access_key = os.environ.get("MINIMAX_API_KEY")
102
+ model = MiniMax_Client(
103
+ model_name, api_key=access_key, user_name=user_name, system_prompt=system_prompt)
104
  elif model_type == ModelType.ChuanhuAgent:
105
  from .ChuanhuAgent import ChuanhuAgent_Client
106
  model = ChuanhuAgent_Client(model_name, access_key, user_name=user_name)
107
+ msg = i18n("启用的工具:") + ", ".join([i.name for i in model.tools])
108
  elif model_type == ModelType.GooglePaLM:
109
+ from .GooglePaLM import Google_PaLM_Client
110
  access_key = os.environ.get("GOOGLE_PALM_API_KEY", access_key)
111
+ model = Google_PaLM_Client(
112
+ model_name, access_key, user_name=user_name)
113
  elif model_type == ModelType.LangchainChat:
114
+ from .Azure import Azure_OpenAI_Client
115
  model = Azure_OpenAI_Client(model_name, user_name=user_name)
116
  elif model_type == ModelType.Midjourney:
117
  from .midjourney import Midjourney_Client
118
  mj_proxy_api_secret = os.getenv("MIDJOURNEY_PROXY_API_SECRET")
119
+ model = Midjourney_Client(
120
+ model_name, mj_proxy_api_secret, user_name=user_name)
121
+ elif model_type == ModelType.Spark:
122
+ from .spark import Spark_Client
123
+ model = Spark_Client(model_name, os.getenv("SPARK_APPID"), os.getenv(
124
+ "SPARK_API_KEY"), os.getenv("SPARK_API_SECRET"), user_name=user_name)
125
+ elif model_type == ModelType.Claude:
126
+ from .Claude import Claude_Client
127
+ model = Claude_Client(model_name="claude-2", api_secret=os.getenv("CLAUDE_API_SECRET"))
128
+ elif model_type == ModelType.Qwen:
129
+ from .Qwen import Qwen_Client
130
+ model = Qwen_Client(model_name, user_name=user_name)
131
  elif model_type == ModelType.Unknown:
132
  raise ValueError(f"未知模型: {model_name}")
133
  logging.info(msg)
 
136
  traceback.print_exc()
137
  msg = f"{STANDARD_ERROR_MSG}: {e}"
138
  presudo_key = hide_middle_chars(access_key)
139
+ if original_model is not None and model is not None:
140
+ model.history = original_model.history
141
+ model.history_file_path = original_model.history_file_path
142
  if dont_change_lora_selector:
143
  return model, msg, chatbot, gr.update(), access_key, presudo_key
144
  else:
modules/models/spark.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import _thread as thread
2
+ import base64
3
+ import datetime
4
+ import hashlib
5
+ import hmac
6
+ import json
7
+ from collections import deque
8
+ from urllib.parse import urlparse
9
+ import ssl
10
+ from datetime import datetime
11
+ from time import mktime
12
+ from urllib.parse import urlencode
13
+ from wsgiref.handlers import format_date_time
14
+ from threading import Condition
15
+ import websocket
16
+ import logging
17
+
18
+ from .base_model import BaseLLMModel, CallbackToIterator
19
+
20
+
21
+ class Ws_Param(object):
22
+ # 来自官方 Demo
23
+ # 初始化
24
+ def __init__(self, APPID, APIKey, APISecret, Spark_url):
25
+ self.APPID = APPID
26
+ self.APIKey = APIKey
27
+ self.APISecret = APISecret
28
+ self.host = urlparse(Spark_url).netloc
29
+ self.path = urlparse(Spark_url).path
30
+ self.Spark_url = Spark_url
31
+
32
+ # 生成url
33
+ def create_url(self):
34
+ # 生成RFC1123格式的时间戳
35
+ now = datetime.now()
36
+ date = format_date_time(mktime(now.timetuple()))
37
+
38
+ # 拼接字符串
39
+ signature_origin = "host: " + self.host + "\n"
40
+ signature_origin += "date: " + date + "\n"
41
+ signature_origin += "GET " + self.path + " HTTP/1.1"
42
+
43
+ # 进行hmac-sha256进行加密
44
+ signature_sha = hmac.new(
45
+ self.APISecret.encode("utf-8"),
46
+ signature_origin.encode("utf-8"),
47
+ digestmod=hashlib.sha256,
48
+ ).digest()
49
+
50
+ signature_sha_base64 = base64.b64encode(
51
+ signature_sha).decode(encoding="utf-8")
52
+
53
+ authorization_origin = f'api_key="{self.APIKey}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature_sha_base64}"'
54
+
55
+ authorization = base64.b64encode(authorization_origin.encode("utf-8")).decode(
56
+ encoding="utf-8"
57
+ )
58
+
59
+ # 将请求的鉴权参数组合为字典
60
+ v = {"authorization": authorization, "date": date, "host": self.host}
61
+ # 拼接鉴权参数,生成url
62
+ url = self.Spark_url + "?" + urlencode(v)
63
+ # 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释,比对相同参数时生成的url与自己代码生成的url是否一致
64
+ return url
65
+
66
+
67
+ class Spark_Client(BaseLLMModel):
68
+ def __init__(self, model_name, appid, api_key, api_secret, user_name="") -> None:
69
+ super().__init__(model_name=model_name, user=user_name)
70
+ self.api_key = api_key
71
+ self.appid = appid
72
+ self.api_secret = api_secret
73
+ if None in [self.api_key, self.appid, self.api_secret]:
74
+ raise Exception("请在配置文件或者环境变量中设置讯飞的API Key、APP ID和API Secret")
75
+ if "2.0" in self.model_name:
76
+ self.spark_url = "wss://spark-api.xf-yun.com/v2.1/chat"
77
+ self.domain = "generalv2"
78
+ if "3.0" in self.model_name:
79
+ self.spark_url = "wss://spark-api.xf-yun.com/v3.1/chat"
80
+ self.domain = "generalv3"
81
+ else:
82
+ self.spark_url = "wss://spark-api.xf-yun.com/v1.1/chat"
83
+ self.domain = "general"
84
+
85
+ # 收到websocket错误的处理
86
+ def on_error(self, ws, error):
87
+ ws.iterator.callback("出现了错误:" + error)
88
+
89
+ # 收到websocket关闭的处理
90
+ def on_close(self, ws, one, two):
91
+ pass
92
+
93
+ # 收到websocket连接建立的处理
94
+ def on_open(self, ws):
95
+ thread.start_new_thread(self.run, (ws,))
96
+
97
+ def run(self, ws, *args):
98
+ data = json.dumps(
99
+ self.gen_params()
100
+ )
101
+ ws.send(data)
102
+
103
+ # 收到websocket消息的处理
104
+ def on_message(self, ws, message):
105
+ ws.iterator.callback(message)
106
+
107
+ def gen_params(self):
108
+ """
109
+ 通过appid和用户的提问来生成请参数
110
+ """
111
+ data = {
112
+ "header": {"app_id": self.appid, "uid": "1234"},
113
+ "parameter": {
114
+ "chat": {
115
+ "domain": self.domain,
116
+ "random_threshold": self.temperature,
117
+ "max_tokens": 4096,
118
+ "auditing": "default",
119
+ }
120
+ },
121
+ "payload": {"message": {"text": self.history}},
122
+ }
123
+ return data
124
+
125
+ def get_answer_stream_iter(self):
126
+ wsParam = Ws_Param(self.appid, self.api_key, self.api_secret, self.spark_url)
127
+ websocket.enableTrace(False)
128
+ wsUrl = wsParam.create_url()
129
+ ws = websocket.WebSocketApp(
130
+ wsUrl,
131
+ on_message=self.on_message,
132
+ on_error=self.on_error,
133
+ on_close=self.on_close,
134
+ on_open=self.on_open,
135
+ )
136
+ ws.appid = self.appid
137
+ ws.domain = self.domain
138
+
139
+ # Initialize the CallbackToIterator
140
+ ws.iterator = CallbackToIterator()
141
+
142
+ # Start the WebSocket connection in a separate thread
143
+ thread.start_new_thread(
144
+ ws.run_forever, (), {"sslopt": {"cert_reqs": ssl.CERT_NONE}}
145
+ )
146
+
147
+ # Iterate over the CallbackToIterator instance
148
+ answer = ""
149
+ total_tokens = 0
150
+ for message in ws.iterator:
151
+ data = json.loads(message)
152
+ code = data["header"]["code"]
153
+ if code != 0:
154
+ ws.close()
155
+ raise Exception(f"请求错误: {code}, {data}")
156
+ else:
157
+ choices = data["payload"]["choices"]
158
+ status = choices["status"]
159
+ content = choices["text"][0]["content"]
160
+ if "usage" in data["payload"]:
161
+ total_tokens = data["payload"]["usage"]["text"]["total_tokens"]
162
+ answer += content
163
+ if status == 2:
164
+ ws.iterator.finish() # Finish the iterator when the status is 2
165
+ ws.close()
166
+ yield answer, total_tokens
modules/overwrites.py CHANGED
@@ -44,32 +44,36 @@ def postprocess_chat_messages(
44
  ) -> str | dict | None:
45
  if chat_message is None:
46
  return None
47
- elif isinstance(chat_message, (tuple, list)):
48
- file_uri = chat_message[0]
49
- if utils.validate_url(file_uri):
50
- filepath = file_uri
51
- else:
52
- filepath = self.make_temp_copy_if_needed(file_uri)
53
-
54
- mime_type = client_utils.get_mimetype(filepath)
55
- return {
56
- "name": filepath,
57
- "mime_type": mime_type,
58
- "alt_text": chat_message[1] if len(chat_message) > 1 else None,
59
- "data": None, # These last two fields are filled in by the frontend
60
- "is_file": True,
61
- }
62
- elif isinstance(chat_message, str):
63
- # chat_message = inspect.cleandoc(chat_message)
64
- # escape html spaces
65
- # chat_message = chat_message.replace(" ", "&nbsp;")
66
- if role == "bot":
67
- chat_message = convert_bot_before_marked(chat_message)
68
- elif role == "user":
69
- chat_message = convert_user_before_marked(chat_message)
70
- return chat_message
71
  else:
72
- raise ValueError(f"Invalid message for Chatbot component: {chat_message}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
 
75
 
@@ -103,4 +107,3 @@ def BlockContext_init(self, *args, **kwargs):
103
 
104
  original_BlockContext_init = gr.blocks.BlockContext.__init__
105
  gr.blocks.BlockContext.__init__ = BlockContext_init
106
-
 
44
  ) -> str | dict | None:
45
  if chat_message is None:
46
  return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  else:
48
+ if isinstance(chat_message, (tuple, list)):
49
+ if len(chat_message) > 0 and "text" in chat_message[0]:
50
+ chat_message = chat_message[0]["text"]
51
+ else:
52
+ file_uri = chat_message[0]
53
+ if utils.validate_url(file_uri):
54
+ filepath = file_uri
55
+ else:
56
+ filepath = self.make_temp_copy_if_needed(file_uri)
57
+
58
+ mime_type = client_utils.get_mimetype(filepath)
59
+ return {
60
+ "name": filepath,
61
+ "mime_type": mime_type,
62
+ "alt_text": chat_message[1] if len(chat_message) > 1 else None,
63
+ "data": None, # These last two fields are filled in by the frontend
64
+ "is_file": True,
65
+ }
66
+ if isinstance(chat_message, str):
67
+ # chat_message = inspect.cleandoc(chat_message)
68
+ # escape html spaces
69
+ # chat_message = chat_message.replace(" ", "&nbsp;")
70
+ if role == "bot":
71
+ chat_message = convert_bot_before_marked(chat_message)
72
+ elif role == "user":
73
+ chat_message = convert_user_before_marked(chat_message)
74
+ return chat_message
75
+ else:
76
+ raise ValueError(f"Invalid message for Chatbot component: {chat_message}")
77
 
78
 
79
 
 
107
 
108
  original_BlockContext_init = gr.blocks.BlockContext.__init__
109
  gr.blocks.BlockContext.__init__ = BlockContext_init
 
modules/presets.py CHANGED
@@ -14,7 +14,9 @@ LLAMA_INFERENCER = None
14
  # ChatGPT 设置
15
  INITIAL_SYSTEM_PROMPT = "You are a helpful assistant."
16
  API_HOST = "api.openai.com"
17
- COMPLETION_URL = "https://api.openai.com/v1/chat/completions"
 
 
18
  BALANCE_API_URL="https://api.openai.com/dashboard/billing/credit_grants"
19
  USAGE_API_URL="https://api.openai.com/dashboard/billing/usage"
20
  HISTORY_DIR = Path("history")
@@ -36,6 +38,7 @@ BILLING_NOT_APPLICABLE_MSG = i18n("账单信息不适用") # 本地运行的模
36
  TIMEOUT_STREAMING = 60 # 流式对话时的超时时间
37
  TIMEOUT_ALL = 200 # 非流式对话时的超时时间
38
  ENABLE_STREAMING_OPTION = True # 是否启用选择选择是否实时显示回答的勾选框
 
39
  HIDE_MY_KEY = False # 如果你想在UI中隐藏你的 API 密钥,将此值设置为 True
40
  CONCURRENT_COUNT = 100 # 允许同时使用的用户数量
41
 
@@ -48,16 +51,15 @@ CHUANHU_DESCRIPTION = i18n("由Bilibili [土川虎虎虎](https://space.bilibili
48
 
49
 
50
  ONLINE_MODELS = [
51
- "gpt-3.5-turbo",
52
- "gpt-3.5-turbo-16k",
53
- "gpt-3.5-turbo-0301",
54
- "gpt-3.5-turbo-0613",
55
- "gpt-4",
56
- "gpt-4-0314",
57
- "gpt-4-0613",
58
- "gpt-4-32k",
59
- "gpt-4-32k-0314",
60
- "gpt-4-32k-0613",
61
  "川虎助理",
62
  "川虎助理 Pro",
63
  "GooglePaLM",
@@ -67,9 +69,12 @@ ONLINE_MODELS = [
67
  "yuanai-1.0-translate",
68
  "yuanai-1.0-dialog",
69
  "yuanai-1.0-rhythm_poems",
70
- "minimax-abab4-chat",
71
  "minimax-abab5-chat",
72
- "midjourney"
 
 
 
 
73
  ]
74
 
75
  LOCAL_MODELS = [
@@ -80,12 +85,69 @@ LOCAL_MODELS = [
80
  "chatglm2-6b-int4",
81
  "StableLM",
82
  "MOSS",
83
- "llama-7b-hf",
84
- "llama-13b-hf",
85
- "llama-30b-hf",
86
- "llama-65b-hf",
87
  ]
88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  if os.environ.get('HIDE_LOCAL_MODELS', 'false') == 'true':
90
  MODELS = ONLINE_MODELS
91
  else:
@@ -101,19 +163,6 @@ for dir_name in os.listdir("models"):
101
  if dir_name not in MODELS:
102
  MODELS.append(dir_name)
103
 
104
- MODEL_TOKEN_LIMIT = {
105
- "gpt-3.5-turbo": 4096,
106
- "gpt-3.5-turbo-16k": 16384,
107
- "gpt-3.5-turbo-0301": 4096,
108
- "gpt-3.5-turbo-0613": 4096,
109
- "gpt-4": 8192,
110
- "gpt-4-0314": 8192,
111
- "gpt-4-0613": 8192,
112
- "gpt-4-32k": 32768,
113
- "gpt-4-32k-0314": 32768,
114
- "gpt-4-32k-0613": 32768
115
- }
116
-
117
  TOKEN_OFFSET = 1000 # 模型的token上限减去这个值,得到软上限。到达软上限之后,自动尝试减少token占用。
118
  DEFAULT_TOKEN_LIMIT = 3000 # 默认的token上限
119
  REDUCE_TOKEN_FACTOR = 0.5 # 与模型token上限想乘,得到目标token数。减少token占用时,将token占用减少到目标token数以下。
@@ -125,11 +174,18 @@ REPLY_LANGUAGES = [
125
  "日本語",
126
  "Español",
127
  "Français",
 
128
  "Deutsch",
129
  "한국어",
130
  "跟随问题语言(不稳定)"
131
  ]
132
 
 
 
 
 
 
 
133
 
134
  WEBSEARCH_PTOMPT_TEMPLATE = """\
135
  Web search results:
@@ -175,6 +231,15 @@ SUMMARIZE_PROMPT = """Write a concise summary of the following:
175
 
176
  CONCISE SUMMARY IN 中文:"""
177
 
 
 
 
 
 
 
 
 
 
178
  ALREADY_CONVERTED_MARK = "<!-- ALREADY CONVERTED BY PARSER. -->"
179
  START_OF_OUTPUT_MARK = "<!-- SOO IN MESSAGE -->"
180
  END_OF_OUTPUT_MARK = "<!-- EOO IN MESSAGE -->"
@@ -243,6 +308,7 @@ small_and_beautiful_theme = gr.themes.Soft(
243
  block_title_background_fill_dark="*primary_900",
244
  block_label_background_fill_dark="*primary_900",
245
  input_background_fill="#F6F6F6",
246
- chatbot_code_background_color="*neutral_950",
 
247
  chatbot_code_background_color_dark="*neutral_950",
248
  )
 
14
  # ChatGPT 设置
15
  INITIAL_SYSTEM_PROMPT = "You are a helpful assistant."
16
  API_HOST = "api.openai.com"
17
+ OPENAI_API_BASE = "https://api.openai.com/v1"
18
+ CHAT_COMPLETION_URL = "https://api.openai.com/v1/chat/completions"
19
+ COMPLETION_URL = "https://api.openai.com/v1/completions"
20
  BALANCE_API_URL="https://api.openai.com/dashboard/billing/credit_grants"
21
  USAGE_API_URL="https://api.openai.com/dashboard/billing/usage"
22
  HISTORY_DIR = Path("history")
 
38
  TIMEOUT_STREAMING = 60 # 流式对话时的超时时间
39
  TIMEOUT_ALL = 200 # 非流式对话时的超时时间
40
  ENABLE_STREAMING_OPTION = True # 是否启用选择选择是否实时显示回答的勾选框
41
+ ENABLE_LLM_NAME_CHAT_OPTION = True # 是否启用选择是否使用LLM模型的勾选框
42
  HIDE_MY_KEY = False # 如果你想在UI中隐藏你的 API 密钥,将此值设置为 True
43
  CONCURRENT_COUNT = 100 # 允许同时使用的用户数量
44
 
 
51
 
52
 
53
  ONLINE_MODELS = [
54
+ "GPT3.5 Turbo",
55
+ "GPT3.5 Turbo Instruct",
56
+ "GPT3.5 Turbo 16K",
57
+ "GPT3.5 Turbo 0301",
58
+ "GPT3.5 Turbo 0613",
59
+ "GPT4",
60
+ "GPT4 32K",
61
+ "GPT4 Turbo",
62
+ "GPT4 Vision",
 
63
  "川虎助理",
64
  "川虎助理 Pro",
65
  "GooglePaLM",
 
69
  "yuanai-1.0-translate",
70
  "yuanai-1.0-dialog",
71
  "yuanai-1.0-rhythm_poems",
 
72
  "minimax-abab5-chat",
73
+ "midjourney",
74
+ "讯飞星火大模型V3.0",
75
+ "讯飞星火大模型V2.0",
76
+ "讯飞星火大模型V1.5",
77
+ "Claude"
78
  ]
79
 
80
  LOCAL_MODELS = [
 
85
  "chatglm2-6b-int4",
86
  "StableLM",
87
  "MOSS",
88
+ "Llama-2-7B-Chat",
89
+ "Qwen 7B",
90
+ "Qwen 14B"
 
91
  ]
92
 
93
+ # Additional metadata for online and local models
94
+ MODEL_METADATA = {
95
+ "Llama-2-7B":{
96
+ "repo_id": "TheBloke/Llama-2-7B-GGUF",
97
+ "filelist": ["llama-2-7b.Q6_K.gguf"],
98
+ },
99
+ "Llama-2-7B-Chat":{
100
+ "repo_id": "TheBloke/Llama-2-7b-Chat-GGUF",
101
+ "filelist": ["llama-2-7b-chat.Q6_K.gguf"],
102
+ },
103
+ "Qwen 7B": {
104
+ "repo_id": "Qwen/Qwen-7B-Chat-Int4",
105
+ },
106
+ "Qwen 14B": {
107
+ "repo_id": "Qwen/Qwen-14B-Chat-Int4",
108
+ },
109
+ "GPT3.5 Turbo": {
110
+ "model_name": "gpt-3.5-turbo",
111
+ "token_limit": 4096,
112
+ },
113
+ "GPT3.5 Turbo Instruct": {
114
+ "model_name": "gpt-3.5-turbo-instruct",
115
+ "token_limit": 4096,
116
+ },
117
+ "GPT3.5 Turbo 16K": {
118
+ "model_name": "gpt-3.5-turbo-16k",
119
+ "token_limit": 16384,
120
+ },
121
+ "GPT3.5 Turbo 0301": {
122
+ "model_name": "gpt-3.5-turbo-0301",
123
+ "token_limit": 4096,
124
+ },
125
+ "GPT3.5 Turbo 0613": {
126
+ "model_name": "gpt-3.5-turbo-0613",
127
+ "token_limit": 4096,
128
+ },
129
+ "GPT4": {
130
+ "model_name": "gpt-4",
131
+ "token_limit": 8192,
132
+ },
133
+ "GPT4 32K": {
134
+ "model_name": "gpt-4-32k",
135
+ "token_limit": 32768,
136
+ },
137
+ "GPT4 Turbo": {
138
+ "model_name": "gpt-4-1106-preview",
139
+ "token_limit": 128000,
140
+ },
141
+ "GPT4 Vision": {
142
+ "model_name": "gpt-4-vision-preview",
143
+ "token_limit": 128000,
144
+ },
145
+ "Claude": {
146
+ "model_name": "Claude",
147
+ "token_limit": 4096,
148
+ },
149
+ }
150
+
151
  if os.environ.get('HIDE_LOCAL_MODELS', 'false') == 'true':
152
  MODELS = ONLINE_MODELS
153
  else:
 
163
  if dir_name not in MODELS:
164
  MODELS.append(dir_name)
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  TOKEN_OFFSET = 1000 # 模型的token上限减去这个值,得到软上限。到达软上限之后,自动尝试减少token占用。
167
  DEFAULT_TOKEN_LIMIT = 3000 # 默认的token上限
168
  REDUCE_TOKEN_FACTOR = 0.5 # 与模型token上限想乘,得到目标token数。减少token占用时,将token占用减少到目标token数以下。
 
174
  "日本語",
175
  "Español",
176
  "Français",
177
+ "Russian",
178
  "Deutsch",
179
  "한국어",
180
  "跟随问题语言(不稳定)"
181
  ]
182
 
183
+ HISTORY_NAME_METHODS = [
184
+ i18n("根据日期时间"),
185
+ i18n("第一条提问"),
186
+ i18n("模型自动总结(消耗tokens)"),
187
+ ]
188
+
189
 
190
  WEBSEARCH_PTOMPT_TEMPLATE = """\
191
  Web search results:
 
231
 
232
  CONCISE SUMMARY IN 中文:"""
233
 
234
+ SUMMARY_CHAT_SYSTEM_PROMPT = """\
235
+ Please summarize the following conversation for a chat topic.
236
+ No more than 16 characters.
237
+ No special characters.
238
+ Punctuation mark is banned.
239
+ Not including '.' ':' '?' '!' '“' '*' '<' '>'.
240
+ Reply in user's language.
241
+ """
242
+
243
  ALREADY_CONVERTED_MARK = "<!-- ALREADY CONVERTED BY PARSER. -->"
244
  START_OF_OUTPUT_MARK = "<!-- SOO IN MESSAGE -->"
245
  END_OF_OUTPUT_MARK = "<!-- EOO IN MESSAGE -->"
 
308
  block_title_background_fill_dark="*primary_900",
309
  block_label_background_fill_dark="*primary_900",
310
  input_background_fill="#F6F6F6",
311
+ # chatbot_code_background_color="*neutral_950",
312
+ # gradio 会把这个几个chatbot打头的变量应用到其他md渲染的地方,鬼晓得怎么想的。。。
313
  chatbot_code_background_color_dark="*neutral_950",
314
  )
modules/repo.py CHANGED
@@ -6,30 +6,33 @@ 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:
@@ -48,29 +51,32 @@ def run(command, desc=None, errdesc=None, custom_env=None, live: bool = default_
48
  error_bits.append(f"stderr: {result.stderr}")
49
  raise RuntimeError("\n".join(error_bits))
50
 
51
- return (result.stdout or "")
52
 
53
 
54
  def run_pip(command, desc=None, pref=None, live=default_command_live):
55
  # if args.skip_install:
56
  # return
57
 
58
- index_url_line = f' --index-url {index_url}' if index_url != '' else ''
59
  return run(
60
- f'"{python}" -m pip {command} --prefer-binary{index_url_line}',
61
- desc=f"{pref} Installing {desc}...",
62
- errdesc=f"Couldn't install {desc}",
63
- live=live
64
  )
65
 
66
 
67
  @lru_cache()
68
  def commit_hash():
69
  try:
70
- return subprocess.check_output([git, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip()
 
 
71
  except Exception:
72
  return "<none>"
73
 
 
74
  def commit_html():
75
  commit = commit_hash()
76
  if commit != "<none>":
@@ -80,6 +86,7 @@ def commit_html():
80
  commit_info = "unknown \U0001F615"
81
  return commit_info
82
 
 
83
  @lru_cache()
84
  def tag_html():
85
  try:
@@ -87,7 +94,7 @@ def tag_html():
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>"
@@ -98,14 +105,16 @@ def tag_html():
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()
@@ -117,20 +126,44 @@ def versions_html():
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
@@ -139,7 +172,10 @@ def get_current_branch():
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
@@ -149,21 +185,34 @@ def get_latest_release():
149
  need_pip = False
150
  return {"tag": tag, "release_note": release_note, "need_pip": need_pip}
151
 
 
152
  def get_tag_commit_hash(tag):
153
  try:
154
  import requests
155
- tags = requests.get("https://api.github.com/repos/GaiZhenbiao/ChuanhuChatGPT/tags").json()
 
 
 
156
  commit_hash = [x["commit"]["sha"] for x in tags if x["name"] == tag][0]
157
  except Exception:
158
  commit_hash = "<none>"
159
  return commit_hash
160
 
 
161
  def repo_need_stash():
162
  try:
163
- return subprocess.check_output([git, "diff-index", "--quiet", "HEAD", "--"], shell=False, encoding='utf8').strip() != ""
 
 
 
 
 
 
 
164
  except Exception:
165
  return True
166
 
 
167
  def background_update():
168
  # {git} fetch --all && ({git} pull https://github.com/GaiZhenbiao/ChuanhuChatGPT.git main -f || ({git} stash && {git} pull https://github.com/GaiZhenbiao/ChuanhuChatGPT.git main -f && {git} stash pop)) && {pip} install -r requirements.txt")
169
  try:
@@ -175,50 +224,83 @@ def background_update():
175
 
176
  timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
177
  current_branch = get_current_branch()
178
- updater_branch = f'tmp_{timestamp}'
179
- backup_branch = f'backup_{timestamp}'
180
  track_repo = "https://github.com/GaiZhenbiao/ChuanhuChatGPT.git"
181
  try:
182
  try:
183
- run(f"{git} fetch {track_repo}", desc="[Updater] Fetching from github...", live=False)
 
 
 
 
184
  except Exception:
185
- logging.error(f"Update failed in fetching, check your network connection")
 
 
186
  return "failed"
187
-
188
- run(f'{git} stash push --include-untracked -m "updater-{timestamp}"',
189
- desc=f'[Updater] Restoring you local changes on stash updater-{timestamp}', live=False) if need_stash else None
190
-
 
 
 
191
  run(f"{git} checkout -b {backup_branch}", live=False)
192
  run(f"{git} checkout -b {updater_branch}", live=False)
193
  run(f"{git} reset --hard FETCH_HEAD", live=False)
194
- run(f"{git} reset --hard {latest_release_hash}", desc=f'[Updater] Checking out {latest_release_tag}...', live=False)
 
 
 
 
195
  run(f"{git} checkout {current_branch}", live=False)
196
-
197
  try:
198
- run(f"{git} merge --no-edit {updater_branch} -q", desc=f"[Updater] Trying to apply latest update on version {latest_release_tag}...")
 
 
 
199
  except Exception:
200
  logging.error(f"Update failed in merging")
201
  try:
202
- run(f"{git} merge --abort", desc="[Updater] Conflict detected, canceling update...")
 
 
 
203
  run(f"{git} reset --hard {backup_branch}", live=False)
204
  run(f"{git} branch -D -f {updater_branch}", live=False)
205
  run(f"{git} branch -D -f {backup_branch}", live=False)
206
  run(f"{git} stash pop", live=False) if need_stash else None
207
- logging.error(f"Update failed, but your file was safely reset to the state before the update.")
 
 
208
  return "failed"
209
  except Exception as e:
210
- logging.error(f"!!!Update failed in resetting, try to reset your files manually. {e}")
 
 
211
  return "failed"
212
 
213
  if need_stash:
214
  try:
215
- run(f"{git} stash apply", desc="[Updater] Trying to restore your local modifications...", live=False)
 
 
 
 
216
  except Exception:
217
- run(f"{git} reset --hard {backup_branch}", desc="[Updater] Conflict detected, canceling update...", live=False)
 
 
 
 
218
  run(f"{git} branch -D -f {updater_branch}", live=False)
219
  run(f"{git} branch -D -f {backup_branch}", live=False)
220
  run(f"{git} stash pop", live=False)
221
- logging.error(f"Update failed in applying your local changes, but your file was safely reset to the state before the update.")
 
 
222
  return "failed"
223
  run(f"{git} stash drop", live=False)
224
 
@@ -228,12 +310,17 @@ def background_update():
228
  logging.error(f"Update failed: {e}")
229
  return "failed"
230
  if need_pip:
231
- try:
232
- run_pip(f"install -r requirements.txt", pref="[Updater]", desc="requirements", live=False)
 
 
 
 
 
233
  except Exception:
234
  logging.error(f"Update failed in pip install")
235
  return "failed"
236
  return "success"
237
  except Exception as e:
238
  logging.error(f"Update failed: {e}")
239
- return "failed"
 
6
  import logging
7
  import gradio as gr
8
  import datetime
9
+ import platform
10
 
11
  # This file is mainly used to describe repo version info, execute the git command, python pip command, shell command, etc.
12
  # Part of the code in this file is referenced from stable-diffusion-webui/modules/launch_utils.py
13
 
14
  python = sys.executable
15
+ pip = os.environ.get("PIP", "pip")
16
+ git = os.environ.get("GIT", "git")
17
 
18
  # Pypi index url
19
+ index_url = os.environ.get("INDEX_URL", "")
20
 
21
  # Whether to default to printing command output
22
  default_command_live = True
23
 
24
 
25
+ def run(
26
+ command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live
27
+ ) -> str:
28
  if desc is not None:
29
  print(desc)
30
  run_kwargs = {
31
  "args": command,
32
  "shell": True,
33
  "env": os.environ if custom_env is None else custom_env,
34
+ "encoding": "utf8",
35
+ "errors": "ignore",
36
  }
37
 
38
  if not live:
 
51
  error_bits.append(f"stderr: {result.stderr}")
52
  raise RuntimeError("\n".join(error_bits))
53
 
54
+ return result.stdout or ""
55
 
56
 
57
  def run_pip(command, desc=None, pref=None, live=default_command_live):
58
  # if args.skip_install:
59
  # return
60
 
61
+ index_url_line = f" --index-url {index_url}" if index_url != "" else ""
62
  return run(
63
+ f'"{python}" -m pip {command} --prefer-binary{index_url_line}',
64
+ desc=f"{pref} Installing {desc}...",
65
+ errdesc=f"Couldn't install {desc}",
66
+ live=live,
67
  )
68
 
69
 
70
  @lru_cache()
71
  def commit_hash():
72
  try:
73
+ return subprocess.check_output(
74
+ [git, "rev-parse", "HEAD"], shell=False, encoding="utf8"
75
+ ).strip()
76
  except Exception:
77
  return "<none>"
78
 
79
+
80
  def commit_html():
81
  commit = commit_hash()
82
  if commit != "<none>":
 
86
  commit_info = "unknown \U0001F615"
87
  return commit_info
88
 
89
+
90
  @lru_cache()
91
  def tag_html():
92
  try:
 
94
  try:
95
  # tag = subprocess.check_output([git, "describe", "--tags", "--exact-match"], shell=False, encoding='utf8').strip()
96
  tag = run(f"{git} describe --tags --exact-match", live=False).strip()
97
+ except Exception:
98
  tag = "<edited>"
99
  except Exception:
100
  tag = "<none>"
 
105
  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>'
106
  else:
107
  tag_info = f'<a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/releases/tag/{tag}">{tag}</a>'
108
+
109
  return tag_info
110
 
111
+
112
  def repo_tag_html():
113
  commit_version = commit_html()
114
  tag_version = tag_html()
115
  return tag_version if tag_version != "unknown \U0001F615" else commit_version
116
 
117
+
118
  def versions_html():
119
  python_version = ".".join([str(x) for x in sys.version_info[0:3]])
120
  repo_version = repo_tag_html()
 
126
  <a style="text-decoration:none;color:inherit" href="https://github.com/GaiZhenbiao/ChuanhuChatGPT">ChuanhuChat</a>: {repo_version}
127
  """
128
 
129
+
130
  def version_time():
131
+ git = "git"
132
+ cmd = f"{git} log -1 --format=%cd --date=iso-strict"
133
+ commit_time = "unknown"
134
  try:
135
+ if platform.system() == "Windows":
136
+ # For Windows
137
+ env = dict(os.environ) # copy the current environment
138
+ env["TZ"] = "UTC" # set timezone to UTC
139
+ raw_commit_time = subprocess.check_output(
140
+ cmd, shell=True, encoding="utf8", env=env
141
+ ).strip()
142
+ else:
143
+ # For Unix systems
144
+ cmd = f"TZ=UTC {cmd}"
145
+ raw_commit_time = subprocess.check_output(
146
+ cmd, shell=True, encoding="utf8"
147
+ ).strip()
148
+
149
+ # Convert the date-time to the desired format
150
+ commit_datetime = datetime.datetime.strptime(
151
+ raw_commit_time, "%Y-%m-%dT%H:%M:%S%z"
152
+ )
153
+ commit_time = commit_datetime.strftime("%Y-%m-%dT%H:%M:%SZ")
154
+
155
+ # logging.info(f"commit time: {commit_time}")
156
  except Exception:
157
  commit_time = "unknown"
158
  return commit_time
159
 
160
 
 
161
  def get_current_branch():
162
  try:
163
  # branch = run(f"{git} rev-parse --abbrev-ref HEAD").strip()
164
+ branch = subprocess.check_output(
165
+ [git, "rev-parse", "--abbrev-ref", "HEAD"], shell=False, encoding="utf8"
166
+ ).strip()
167
  except Exception:
168
  branch = "<none>"
169
  return branch
 
172
  def get_latest_release():
173
  try:
174
  import requests
175
+
176
+ release = requests.get(
177
+ "https://api.github.com/repos/GaiZhenbiao/ChuanhuChatGPT/releases/latest"
178
+ ).json()
179
  tag = release["tag_name"]
180
  release_note = release["body"]
181
  need_pip = release_note.find("requirements reinstall needed") != -1
 
185
  need_pip = False
186
  return {"tag": tag, "release_note": release_note, "need_pip": need_pip}
187
 
188
+
189
  def get_tag_commit_hash(tag):
190
  try:
191
  import requests
192
+
193
+ tags = requests.get(
194
+ "https://api.github.com/repos/GaiZhenbiao/ChuanhuChatGPT/tags"
195
+ ).json()
196
  commit_hash = [x["commit"]["sha"] for x in tags if x["name"] == tag][0]
197
  except Exception:
198
  commit_hash = "<none>"
199
  return commit_hash
200
 
201
+
202
  def repo_need_stash():
203
  try:
204
+ return (
205
+ subprocess.check_output(
206
+ [git, "diff-index", "--quiet", "HEAD", "--"],
207
+ shell=False,
208
+ encoding="utf8",
209
+ ).strip()
210
+ != ""
211
+ )
212
  except Exception:
213
  return True
214
 
215
+
216
  def background_update():
217
  # {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")
218
  try:
 
224
 
225
  timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
226
  current_branch = get_current_branch()
227
+ updater_branch = f"tmp_{timestamp}"
228
+ backup_branch = f"backup_{timestamp}"
229
  track_repo = "https://github.com/GaiZhenbiao/ChuanhuChatGPT.git"
230
  try:
231
  try:
232
+ run(
233
+ f"{git} fetch {track_repo}",
234
+ desc="[Updater] Fetching from github...",
235
+ live=False,
236
+ )
237
  except Exception:
238
+ logging.error(
239
+ f"Update failed in fetching, check your network connection"
240
+ )
241
  return "failed"
242
+
243
+ run(
244
+ f'{git} stash push --include-untracked -m "updater-{timestamp}"',
245
+ desc=f"[Updater] Restoring you local changes on stash updater-{timestamp}",
246
+ live=False,
247
+ ) if need_stash else None
248
+
249
  run(f"{git} checkout -b {backup_branch}", live=False)
250
  run(f"{git} checkout -b {updater_branch}", live=False)
251
  run(f"{git} reset --hard FETCH_HEAD", live=False)
252
+ run(
253
+ f"{git} reset --hard {latest_release_hash}",
254
+ desc=f"[Updater] Checking out {latest_release_tag}...",
255
+ live=False,
256
+ )
257
  run(f"{git} checkout {current_branch}", live=False)
258
+
259
  try:
260
+ run(
261
+ f"{git} merge --no-edit {updater_branch} -q",
262
+ desc=f"[Updater] Trying to apply latest update on version {latest_release_tag}...",
263
+ )
264
  except Exception:
265
  logging.error(f"Update failed in merging")
266
  try:
267
+ run(
268
+ f"{git} merge --abort",
269
+ desc="[Updater] Conflict detected, canceling update...",
270
+ )
271
  run(f"{git} reset --hard {backup_branch}", live=False)
272
  run(f"{git} branch -D -f {updater_branch}", live=False)
273
  run(f"{git} branch -D -f {backup_branch}", live=False)
274
  run(f"{git} stash pop", live=False) if need_stash else None
275
+ logging.error(
276
+ f"Update failed, but your file was safely reset to the state before the update."
277
+ )
278
  return "failed"
279
  except Exception as e:
280
+ logging.error(
281
+ f"!!!Update failed in resetting, try to reset your files manually. {e}"
282
+ )
283
  return "failed"
284
 
285
  if need_stash:
286
  try:
287
+ run(
288
+ f"{git} stash apply",
289
+ desc="[Updater] Trying to restore your local modifications...",
290
+ live=False,
291
+ )
292
  except Exception:
293
+ run(
294
+ f"{git} reset --hard {backup_branch}",
295
+ desc="[Updater] Conflict detected, canceling update...",
296
+ live=False,
297
+ )
298
  run(f"{git} branch -D -f {updater_branch}", live=False)
299
  run(f"{git} branch -D -f {backup_branch}", live=False)
300
  run(f"{git} stash pop", live=False)
301
+ logging.error(
302
+ f"Update failed in applying your local changes, but your file was safely reset to the state before the update."
303
+ )
304
  return "failed"
305
  run(f"{git} stash drop", live=False)
306
 
 
310
  logging.error(f"Update failed: {e}")
311
  return "failed"
312
  if need_pip:
313
+ try:
314
+ run_pip(
315
+ f"install -r requirements.txt",
316
+ pref="[Updater]",
317
+ desc="requirements",
318
+ live=False,
319
+ )
320
  except Exception:
321
  logging.error(f"Update failed in pip install")
322
  return "failed"
323
  return "success"
324
  except Exception as e:
325
  logging.error(f"Update failed: {e}")
326
+ return "failed"
modules/shared.py CHANGED
@@ -1,4 +1,4 @@
1
- from modules.presets import COMPLETION_URL, BALANCE_API_URL, USAGE_API_URL, API_HOST
2
  import os
3
  import queue
4
  import openai
@@ -6,9 +6,10 @@ import openai
6
  class State:
7
  interrupted = False
8
  multi_api_key = False
9
- completion_url = COMPLETION_URL
10
  balance_api_url = BALANCE_API_URL
11
  usage_api_url = USAGE_API_URL
 
12
 
13
  def interrupt(self):
14
  self.interrupted = True
@@ -22,13 +23,14 @@ class State:
22
  api_host = f"https://{api_host}"
23
  if api_host.endswith("/v1"):
24
  api_host = api_host[:-3]
25
- self.completion_url = f"{api_host}/v1/chat/completions"
 
26
  self.balance_api_url = f"{api_host}/dashboard/billing/credit_grants"
27
  self.usage_api_url = f"{api_host}/dashboard/billing/usage"
28
  os.environ["OPENAI_API_BASE"] = api_host
29
 
30
  def reset_api_host(self):
31
- self.completion_url = COMPLETION_URL
32
  self.balance_api_url = BALANCE_API_URL
33
  self.usage_api_url = USAGE_API_URL
34
  os.environ["OPENAI_API_BASE"] = f"https://{API_HOST}"
@@ -36,7 +38,7 @@ class State:
36
 
37
  def reset_all(self):
38
  self.interrupted = False
39
- self.completion_url = COMPLETION_URL
40
 
41
  def set_api_key_queue(self, api_key_list):
42
  self.multi_api_key = True
 
1
+ from modules.presets import CHAT_COMPLETION_URL, BALANCE_API_URL, USAGE_API_URL, API_HOST, OPENAI_API_BASE
2
  import os
3
  import queue
4
  import openai
 
6
  class State:
7
  interrupted = False
8
  multi_api_key = False
9
+ chat_completion_url = CHAT_COMPLETION_URL
10
  balance_api_url = BALANCE_API_URL
11
  usage_api_url = USAGE_API_URL
12
+ openai_api_base = OPENAI_API_BASE
13
 
14
  def interrupt(self):
15
  self.interrupted = True
 
23
  api_host = f"https://{api_host}"
24
  if api_host.endswith("/v1"):
25
  api_host = api_host[:-3]
26
+ self.chat_completion_url = f"{api_host}/v1/chat/completions"
27
+ self.openai_api_base = f"{api_host}/v1"
28
  self.balance_api_url = f"{api_host}/dashboard/billing/credit_grants"
29
  self.usage_api_url = f"{api_host}/dashboard/billing/usage"
30
  os.environ["OPENAI_API_BASE"] = api_host
31
 
32
  def reset_api_host(self):
33
+ self.chat_completion_url = CHAT_COMPLETION_URL
34
  self.balance_api_url = BALANCE_API_URL
35
  self.usage_api_url = USAGE_API_URL
36
  os.environ["OPENAI_API_BASE"] = f"https://{API_HOST}"
 
38
 
39
  def reset_all(self):
40
  self.interrupted = False
41
+ self.chat_completion_url = CHAT_COMPLETION_URL
42
 
43
  def set_api_key_queue(self, api_key_list):
44
  self.multi_api_key = True
modules/utils.py CHANGED
@@ -68,15 +68,15 @@ def delete_last_conversation(current_model, *args):
68
  def set_system_prompt(current_model, *args):
69
  return current_model.set_system_prompt(*args)
70
 
71
- def save_chat_history(current_model, *args):
72
- return current_model.save_chat_history(*args)
 
 
 
73
 
74
  def export_markdown(current_model, *args):
75
  return current_model.export_markdown(*args)
76
 
77
- def load_chat_history(current_model, *args):
78
- return current_model.load_chat_history(*args)
79
-
80
  def upload_chat_history(current_model, *args):
81
  return current_model.load_chat_history(*args)
82
 
@@ -331,55 +331,97 @@ def construct_assistant(text):
331
 
332
 
333
  def save_file(filename, system, history, chatbot, user_name):
334
- logging.debug(f"{user_name} 保存对话历史中……")
335
  os.makedirs(os.path.join(HISTORY_DIR, user_name), exist_ok=True)
336
- if filename.endswith(".json"):
337
- json_s = {"system": system, "history": history, "chatbot": chatbot}
338
- if "/" in filename or "\\" in filename:
339
- history_file_path = filename
340
- else:
341
- history_file_path = os.path.join(HISTORY_DIR, user_name, filename)
342
- with open(history_file_path, "w", encoding='utf-8') as f:
343
- json.dump(json_s, f, ensure_ascii=False)
344
- elif filename.endswith(".md"):
345
- md_s = f"system: \n- {system} \n"
346
- for data in history:
347
- md_s += f"\n{data['role']}: \n- {data['content']} \n"
348
- with open(os.path.join(HISTORY_DIR, user_name, filename), "w", encoding="utf8") as f:
349
- f.write(md_s)
350
- logging.debug(f"{user_name} 保存对话历史完毕")
 
 
 
 
 
 
 
 
 
 
 
351
  return os.path.join(HISTORY_DIR, user_name, filename)
352
 
353
 
354
  def sorted_by_pinyin(list):
355
  return sorted(list, key=lambda char: lazy_pinyin(char)[0][0])
356
 
 
 
357
 
358
- def get_file_names(dir, plain=False, filetypes=[".json"]):
359
- logging.debug(f"获取文件名列表,目录为{dir},文件类型为{filetypes},是否为纯文本列表{plain}")
360
  files = []
361
- try:
362
- for type in filetypes:
363
- files += [f for f in os.listdir(dir) if f.endswith(type)]
364
- except FileNotFoundError:
365
- files = []
366
- files = sorted_by_pinyin(files)
367
- if files == []:
368
- files = [""]
369
  logging.debug(f"files are:{files}")
370
- if plain:
371
- return files
372
- else:
373
- return gr.Dropdown.update(choices=files)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
 
375
 
376
- def get_history_names(plain=False, user_name=""):
377
  logging.debug(f"从用户 {user_name} 中获取历史记录文件名列表")
378
  if user_name == "" and hide_history_when_not_logged_in:
379
- return ""
380
  else:
381
- return get_file_names(os.path.join(HISTORY_DIR, user_name), plain)
 
 
 
 
 
 
382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
 
384
  def load_template(filename, mode=0):
385
  logging.debug(f"加载模板文件{filename},模式为{mode}(0为返回字典和下拉菜单,1为返回下拉菜单,2为返回字典)")
@@ -406,9 +448,14 @@ def load_template(filename, mode=0):
406
  )
407
 
408
 
409
- def get_template_names(plain=False):
410
  logging.debug("获取模板文件名列表")
411
- return get_file_names(TEMPLATES_DIR, plain, filetypes=[".csv", "json"])
 
 
 
 
 
412
 
413
 
414
  def get_template_content(templates, selection, original_system_prompt):
@@ -614,36 +661,21 @@ def toggle_like_btn_visibility(selected_model_name):
614
  else:
615
  return gr.update(visible=False)
616
 
617
- def new_auto_history_filename(dirname):
618
- latest_file = get_latest_filepath(dirname)
619
  if latest_file:
620
- with open(os.path.join(dirname, latest_file), 'r', encoding="utf-8") as f:
621
  if len(f.read()) == 0:
622
  return latest_file
623
- now = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
624
  return f'{now}.json'
625
 
626
- def get_latest_filepath(dirname):
627
- pattern = re.compile(r'\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}')
628
- latest_time = None
629
- latest_file = None
630
- for filename in os.listdir(dirname):
631
- if os.path.isfile(os.path.join(dirname, filename)):
632
- match = pattern.search(filename)
633
- if match and match.group(0) == filename[:19]:
634
- time_str = filename[:19]
635
- filetime = datetime.datetime.strptime(time_str, '%Y-%m-%d_%H-%M-%S')
636
- if not latest_time or filetime > latest_time:
637
- latest_time = filetime
638
- latest_file = filename
639
- return latest_file
640
-
641
  def get_history_filepath(username):
642
  dirname = os.path.join(HISTORY_DIR, username)
643
  os.makedirs(dirname, exist_ok=True)
644
- latest_file = get_latest_filepath(dirname)
645
  if not latest_file:
646
- latest_file = new_auto_history_filename(dirname)
647
 
648
  latest_file = os.path.join(dirname, latest_file)
649
  return latest_file
@@ -651,7 +683,7 @@ def get_history_filepath(username):
651
  def beautify_err_msg(err_msg):
652
  if "insufficient_quota" in err_msg:
653
  return i18n("剩余配额不足,[进一步了解](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98#you-exceeded-your-current-quota-please-check-your-plan-and-billing-details)")
654
- if "The model: gpt-4 does not exist" in err_msg:
655
  return i18n("你没有权限访问 GPT4,[进一步了解](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)")
656
  if "Resource not found" in err_msg:
657
  return i18n("请查看 config_example.json,配置 Azure OpenAI")
@@ -681,3 +713,14 @@ def get_file_hash(file_src=None, file_paths=None):
681
  md5_hash.update(chunk)
682
 
683
  return md5_hash.hexdigest()
 
 
 
 
 
 
 
 
 
 
 
 
68
  def set_system_prompt(current_model, *args):
69
  return current_model.set_system_prompt(*args)
70
 
71
+ def rename_chat_history(current_model, *args):
72
+ return current_model.rename_chat_history(*args)
73
+
74
+ def auto_name_chat_history(current_model, *args):
75
+ return current_model.auto_name_chat_history(*args)
76
 
77
  def export_markdown(current_model, *args):
78
  return current_model.export_markdown(*args)
79
 
 
 
 
80
  def upload_chat_history(current_model, *args):
81
  return current_model.load_chat_history(*args)
82
 
 
331
 
332
 
333
  def save_file(filename, system, history, chatbot, user_name):
 
334
  os.makedirs(os.path.join(HISTORY_DIR, user_name), exist_ok=True)
335
+ if filename is None:
336
+ filename = new_auto_history_filename(user_name)
337
+ if filename.endswith(".md"):
338
+ filename = filename[:-3]
339
+ if not filename.endswith(".json") and not filename.endswith(".md"):
340
+ filename += ".json"
341
+ if filename == ".json":
342
+ raise Exception("文件名不能为空")
343
+
344
+ json_s = {"system": system, "history": history, "chatbot": chatbot}
345
+ repeat_file_index = 2
346
+ if not filename == os.path.basename(filename):
347
+ history_file_path = filename
348
+ else:
349
+ history_file_path = os.path.join(HISTORY_DIR, user_name, filename)
350
+
351
+ with open(history_file_path, "w", encoding='utf-8') as f:
352
+ json.dump(json_s, f, ensure_ascii=False)
353
+
354
+ filename = os.path.basename(filename)
355
+ filename_md = filename[:-5] + ".md"
356
+ md_s = f"system: \n- {system} \n"
357
+ for data in history:
358
+ md_s += f"\n{data['role']}: \n- {data['content']} \n"
359
+ with open(os.path.join(HISTORY_DIR, user_name, filename_md), "w", encoding="utf8") as f:
360
+ f.write(md_s)
361
  return os.path.join(HISTORY_DIR, user_name, filename)
362
 
363
 
364
  def sorted_by_pinyin(list):
365
  return sorted(list, key=lambda char: lazy_pinyin(char)[0][0])
366
 
367
+ def sorted_by_last_modified_time(list, dir):
368
+ return sorted(list, key=lambda char: os.path.getctime(os.path.join(dir, char)), reverse=True)
369
 
370
+ def get_file_names_by_type(dir, filetypes=[".json"]):
371
+ logging.debug(f"获取文件名列表,目录为{dir},文件类型为{filetypes}")
372
  files = []
373
+ for type in filetypes:
374
+ files += [f for f in os.listdir(dir) if f.endswith(type)]
 
 
 
 
 
 
375
  logging.debug(f"files are:{files}")
376
+ return files
377
+
378
+ def get_file_names_by_pinyin(dir, filetypes=[".json"]):
379
+ files = get_file_names_by_type(dir, filetypes)
380
+ if files != [""]:
381
+ files = sorted_by_pinyin(files)
382
+ logging.debug(f"files are:{files}")
383
+ return files
384
+
385
+ def get_file_names_dropdown_by_pinyin(dir, filetypes=[".json"]):
386
+ files = get_file_names_by_pinyin(dir, filetypes)
387
+ return gr.Dropdown.update(choices=files)
388
+
389
+ def get_file_names_by_last_modified_time(dir, filetypes=[".json"]):
390
+ files = get_file_names_by_type(dir, filetypes)
391
+ if files != [""]:
392
+ files = sorted_by_last_modified_time(files, dir)
393
+ logging.debug(f"files are:{files}")
394
+ return files
395
 
396
 
397
+ def get_history_names(user_name=""):
398
  logging.debug(f"从用户 {user_name} 中获取历史记录文件名列表")
399
  if user_name == "" and hide_history_when_not_logged_in:
400
+ return []
401
  else:
402
+ history_files = get_file_names_by_last_modified_time(os.path.join(HISTORY_DIR, user_name))
403
+ history_files = [f[:f.rfind(".")] for f in history_files]
404
+ return history_files
405
+
406
+ def get_first_history_name(user_name=""):
407
+ history_names = get_history_names(user_name)
408
+ return history_names[0] if history_names else None
409
 
410
+ def get_history_list(user_name=""):
411
+ history_names = get_history_names(user_name)
412
+ return gr.Radio.update(choices=history_names)
413
+
414
+ def init_history_list(user_name=""):
415
+ history_names = get_history_names(user_name)
416
+ return gr.Radio.update(choices=history_names, value=history_names[0] if history_names else "")
417
+
418
+ def filter_history(user_name, keyword):
419
+ history_names = get_history_names(user_name)
420
+ try:
421
+ history_names = [name for name in history_names if re.search(keyword, name)]
422
+ return gr.update(choices=history_names)
423
+ except:
424
+ return gr.update(choices=history_names)
425
 
426
  def load_template(filename, mode=0):
427
  logging.debug(f"加载模板文件{filename},模式为{mode}(0为返回字典和下拉菜单,1为返回下拉菜单,2为返回字典)")
 
448
  )
449
 
450
 
451
+ def get_template_names():
452
  logging.debug("获取模板文件名列表")
453
+ return get_file_names_by_pinyin(TEMPLATES_DIR, filetypes=[".csv", "json"])
454
+
455
+ def get_template_dropdown():
456
+ logging.debug("获取模板下拉菜单")
457
+ template_names = get_template_names()
458
+ return gr.Dropdown.update(choices=template_names)
459
 
460
 
461
  def get_template_content(templates, selection, original_system_prompt):
 
661
  else:
662
  return gr.update(visible=False)
663
 
664
+ def new_auto_history_filename(username):
665
+ latest_file = get_first_history_name(username)
666
  if latest_file:
667
+ with open(os.path.join(HISTORY_DIR, username, latest_file + ".json"), 'r', encoding="utf-8") as f:
668
  if len(f.read()) == 0:
669
  return latest_file
670
+ now = i18n("新对话 ") + datetime.datetime.now().strftime('%m-%d %H-%M')
671
  return f'{now}.json'
672
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
673
  def get_history_filepath(username):
674
  dirname = os.path.join(HISTORY_DIR, username)
675
  os.makedirs(dirname, exist_ok=True)
676
+ latest_file = get_first_history_name(username)
677
  if not latest_file:
678
+ latest_file = new_auto_history_filename(username)
679
 
680
  latest_file = os.path.join(dirname, latest_file)
681
  return latest_file
 
683
  def beautify_err_msg(err_msg):
684
  if "insufficient_quota" in err_msg:
685
  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)")
686
+ if "The model `gpt-4` does not exist" in err_msg:
687
  return i18n("你没有权限访问 GPT4,[进一步了解](https://github.com/GaiZhenbiao/ChuanhuChatGPT/issues/843)")
688
  if "Resource not found" in err_msg:
689
  return i18n("请查看 config_example.json,配置 Azure OpenAI")
 
713
  md5_hash.update(chunk)
714
 
715
  return md5_hash.hexdigest()
716
+
717
+ def myprint(**args):
718
+ print(args)
719
+
720
+ def replace_special_symbols(string, replace_string=" "):
721
+ # 定义正则表达式,匹配所有特殊符号
722
+ pattern = r'[!@#$%^&*()<>?/\|}{~:]'
723
+
724
+ new_string = re.sub(pattern, replace_string, string)
725
+
726
+ return new_string
modules/webui.py CHANGED
@@ -56,11 +56,24 @@ def reload_javascript():
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
 
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
+ meta = """
60
+ <meta name="apple-mobile-web-app-title" content="川虎 Chat">
61
+ <meta name="apple-mobile-web-app-capable" content="yes">
62
+ <meta name="application-name" content="川虎 Chat">
63
+ <meta name='viewport' content='width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover'>
64
+ <meta name="theme-color" content="#ffffff">
65
+
66
+ <link rel="apple-touch-icon-precomposed" href="/file=web_assets/icon/mask-icon-512.png" crossorigin="use-credentials">
67
+ <link rel="apple-touch-icon" href="/file=web_assets/icon/mask-icon-512.png" crossorigin="use-credentials">
68
+
69
+ <link rel="manifest" href="/file=web_assets/manifest.json" crossorigin="use-credentials">
70
+ """
71
  css = css_html()
72
 
73
  def template_response(*args, **kwargs):
74
  res = GradioTemplateResponseOriginal(*args, **kwargs)
75
+ res.body = res.body.replace(b'</head>', f'{meta}{js}</head>'.encode("utf8"))
76
+ # res.body = res.body.replace(b'</head>', f'{js}</head>'.encode("utf8"))
77
  res.body = res.body.replace(b'</body>', f'{css}</body>'.encode("utf8"))
78
  res.init_headers()
79
  return res
modules/webui_locale.py CHANGED
@@ -1,5 +1,6 @@
1
  import os
2
  import locale
 
3
  import commentjson as json
4
 
5
  class I18nAuto:
@@ -9,8 +10,9 @@ class I18nAuto:
9
  config = json.load(f)
10
  else:
11
  config = {}
12
- lang_config = config.get("language", "auto")
13
- language = os.environ.get("LANGUAGE", lang_config)
 
14
  if language == "auto":
15
  language = locale.getdefaultlocale()[0] # get the language code of the system (ex. zh_CN)
16
  self.language_map = {}
@@ -18,6 +20,11 @@ class I18nAuto:
18
  if self.file_is_exists:
19
  with open(f"./locale/{language}.json", "r", encoding="utf-8") as f:
20
  self.language_map.update(json.load(f))
 
 
 
 
 
21
 
22
  def __call__(self, key):
23
  if self.file_is_exists and key in self.language_map:
 
1
  import os
2
  import locale
3
+ import logging
4
  import commentjson as json
5
 
6
  class I18nAuto:
 
10
  config = json.load(f)
11
  else:
12
  config = {}
13
+ language = config.get("language", "auto")
14
+ language = os.environ.get("LANGUAGE", language)
15
+ language = language.replace("-", "_")
16
  if language == "auto":
17
  language = locale.getdefaultlocale()[0] # get the language code of the system (ex. zh_CN)
18
  self.language_map = {}
 
20
  if self.file_is_exists:
21
  with open(f"./locale/{language}.json", "r", encoding="utf-8") as f:
22
  self.language_map.update(json.load(f))
23
+ else:
24
+ logging.warning(f"Language file for {language} does not exist. Using English instead.")
25
+ logging.warning(f"Available languages: {', '.join([x[:-5] for x in os.listdir('./locale')])}")
26
+ with open(f"./locale/en_US.json", "r", encoding="utf-8") as f:
27
+ self.language_map.update(json.load(f))
28
 
29
  def __call__(self, key):
30
  if self.file_is_exists and key in self.language_map:
readme/README_en.md CHANGED
@@ -1,6 +1,6 @@
1
  <div align="right">
2
  <!-- Language: -->
3
- <a title="Chinese" href="../README.md">简体中文</a> | English | <a title="Japanese" href="README_ja.md">日本語</a>
4
  </div>
5
 
6
  <h1 align="center">川虎 Chat 🐯 Chuanhu Chat</h1>
@@ -22,11 +22,7 @@
22
  <img alt="GitHub pull requests" src="https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram" />
23
  </a>
24
  <p>
25
- Streaming / Unlimited conversations / Save history / Preset prompts / Chat with files / Web search <br />
26
- LaTeX rendering / Table rendering / Code highlighting <br />
27
- Auto dark mode / Adaptive web interface / WeChat-like theme <br />
28
- Multi-parameters tuning / Multi-API-Key support / Multi-user support <br />
29
- Compatible with GPT-4 / Local deployment for LLMs
30
  </p>
31
  <a href="https://www.youtube.com/watch?v=MtxS4XZWbJE"><strong>Video Tutorial</strong></a>
32
  ·
@@ -38,41 +34,94 @@
38
  ·
39
  <a href="https://huggingface.co/login?next=%2Fspaces%2FJohnSmith9982%2FChuanhuChatGPT%3Fduplicate%3Dtrue"><strong>One-Click deployment</strong></a>
40
  </p>
41
- <p align="center">
42
- <img alt="Animation Demo" src="https://user-images.githubusercontent.com/51039745/226255695-6b17ff1f-ea8d-464f-b69b-a7b6b68fffe8.gif" />
43
- </p>
44
  </p>
45
  </div>
46
 
47
- ## Supported LLM Models
48
 
49
- **LLM models via API**:
50
 
51
- - [ChatGPT](https://chat.openai.com) ([GPT-4](https://openai.com/product/gpt-4))
52
- - [Google PaLM](https://developers.generativeai.google/products/palm)
53
- - [Inspur Yuan 1.0](https://air.inspur.com/home)
54
- - [MiniMax](https://api.minimax.chat/)
55
- - [XMChat](https://github.com/MILVLG/xmchat)
56
 
57
- **LLM models via local deployment**:
58
 
59
- - [ChatGLM](https://github.com/THUDM/ChatGLM-6B) ([ChatGLM2](https://github.com/THUDM/ChatGLM2-6B))
60
- - [LLaMA](https://github.com/facebookresearch/llama)
61
- - [StableLM](https://github.com/Stability-AI/StableLM)
62
- - [MOSS](https://github.com/OpenLMLab/MOSS)
63
 
64
- ## Usage Tips
 
 
 
 
 
 
65
 
66
- - To better control the ChatGPT, use System Prompt.
67
- - To use a Prompt Template, select the Prompt Template Collection file first, and then choose certain prompt from the drop-down menu.
68
- - To try again if the response is unsatisfactory, use `🔄 Regenerate` button.
69
- - To start a new line in the input box, press <kbd>Shift</kbd> + <kbd>Enter</kbd> keys.
70
- - To quickly switch between input history, press <kbd>↑</kbd> and <kbd>↓</kbd> key in the input box.
71
- - To deploy the program onto a server, set `"server_name": "0.0.0.0", "server_port" <your port number>,` in `config.json`.
72
- - To get a public shared link, set `"share": true,` in `config.json`. Please be noted that the program must be running in order to be accessed via a public link.
73
- - To use it in Hugging Face Spaces: It is recommended to **Duplicate Space** and run the program in your own Space for a faster and more secure experience.
74
 
75
- ## Quickstart
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
  ```shell
78
  git clone https://github.com/GaiZhenbiao/ChuanhuChatGPT.git
@@ -86,21 +135,22 @@ Then make a copy of `config_example.json`, rename it to `config.json`, and then
86
  python ChuanhuChatbot.py
87
  ```
88
 
89
- A browser window will open and you will be able to chat with ChatGPT.
90
 
91
  > **Note**
92
  >
93
- > Please check our [wiki page](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程) for detailed instructions.
 
94
 
95
  ## Troubleshooting
96
 
97
- When you encounter problems, you should try manually pulling the latest changes of this project first. The steps are as follows:
98
 
99
- 1. Download the latest code archive by clicking on `Download ZIP` on the webpage, or
100
  ```shell
101
  git pull https://github.com/GaiZhenbiao/ChuanhuChatGPT.git main -f
102
  ```
103
- 2. Try installing the dependencies again (as this project may have introduced new dependencies)
104
  ```
105
  pip install -r requirements.txt
106
  ```
 
1
  <div align="right">
2
  <!-- Language: -->
3
+ <a title="Chinese" href="../README.md">简体中文</a> | English | <a title="Japanese" href="README_ja.md">日本語</a> | <a title="Russian" href="README_ru.md">Russian</a>
4
  </div>
5
 
6
  <h1 align="center">川虎 Chat 🐯 Chuanhu Chat</h1>
 
22
  <img alt="GitHub pull requests" src="https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram" />
23
  </a>
24
  <p>
25
+ Compatible with GPT-4 · Chat with files · LLMs local deployment · Web search · Chuanhu Agent · Fine-tuning
 
 
 
 
26
  </p>
27
  <a href="https://www.youtube.com/watch?v=MtxS4XZWbJE"><strong>Video Tutorial</strong></a>
28
  ·
 
34
  ·
35
  <a href="https://huggingface.co/login?next=%2Fspaces%2FJohnSmith9982%2FChuanhuChatGPT%3Fduplicate%3Dtrue"><strong>One-Click deployment</strong></a>
36
  </p>
 
 
 
37
  </p>
38
  </div>
39
 
40
+ [![Video Title](https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/51039745/0eee1598-c2fd-41c6-bda9-7b059a3ce6e7.jpg)](https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/51039745/0eee1598-c2fd-41c6-bda9-7b059a3ce6e7?autoplay=1)
41
 
42
+ ## 5.0 Major Update!
43
 
44
+ ![ChuanhuChat5update](https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/70903329/f2c2be3a-ea93-4edf-8221-94eddd4a0178)
 
 
 
 
45
 
 
46
 
47
+ <sup>New!</sup> An all-new user interface! So exquisite that it doesn't look like Gradio, it even has a frosted glass effect!
 
 
 
48
 
49
+ <sup>New!</sup> Adapted for mobile devices (including perforated/bezel-less phones), the hierarchy is clearer.
50
+
51
+ <sup>New!</sup> The history is moved to the left for easier use. And supports search (with regular expressions), delete, and rename.
52
+
53
+ <sup>New!</sup> Now you can let the large model automatically name the history (Enabled in the settings or configuration file).
54
+
55
+ <sup>New!</sup> Chuanhu Chat can now be installed as a PWA application for a more native experience! Supported on Chrome/Edge/Safari etc.
56
 
57
+ <sup>New!</sup> Icons adapted for all platforms, looking more comfortable.
 
 
 
 
 
 
 
58
 
59
+ <sup>New!</sup> Supports Finetune (fine-tuning) GPT 3.5!
60
+
61
+ ## Supported Models
62
+
63
+ | API Callable Models | Remarks | Locally Deployed Models | Remarks |
64
+ | :---: | --- | :---: | --- |
65
+ | [ChatGPT(GPT-4)](https://chat.openai.com) | Support fine-tune gpt-3.5 | [ChatGLM](https://github.com/THUDM/ChatGLM-6B) ([ChatGLM2](https://github.com/THUDM/ChatGLM2-6B)) |
66
+ | [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service) | | [LLaMA](https://github.com/facebookresearch/llama) | Support Lora models
67
+ | [Google PaLM](https://developers.generativeai.google/products/palm) | Not support streaming | [StableLM](https://github.com/Stability-AI/StableLM)
68
+ | [iFlytek Starfire Cognition Large Model](https://xinghuo.xfyun.cn) | | [MOSS](https://github.com/OpenLMLab/MOSS)
69
+ | [Inspur Yuan 1.0](https://air.inspur.com/home) | | [Qwen](https://github.com/QwenLM/Qwen/tree/main)
70
+ | [MiniMax](https://api.minimax.chat/) |
71
+ | [XMChat](https://github.com/MILVLG/xmchat) | Not support streaming
72
+ | [Midjourney](https://www.midjourney.com/) | Not support streaming
73
+ | [Claude](https://www.anthropic.com/) |
74
+
75
+ ## Usage Tips
76
+
77
+ ### 💪 Powerful Functions
78
+ - **Chuanhu Assistant**: Similar to AutoGPT, automatically solves your problems;
79
+ - **Online Search**: Is ChatGPT's data too old? Give LLM the wings of the internet;
80
+ - **Knowledge Base**: Let ChatGPT help you speed read quantumly! Answer questions based on files.
81
+ - **Local LLM Deployment**: One-click deployment, get your own large language model.
82
+
83
+ ### 🤖 System Prompt
84
+ - The system prompt can effectively enable role-playing by setting prerequisite conditions;
85
+ - ChuanhuChat presets Prompt templates, click `Load Prompt Template`, choose the Prompt template collection first, then choose the Prompt you want in the list below.
86
+
87
+ ### 💬 Basic Conversation
88
+ - If the answer is not satisfactory, you can try the `Regenerate` button again, or directly `Delete this round of conversation`;
89
+ - Input box supports line breaks, press <kbd>Shift</kbd> + <kbd>Enter</kbd> to make one;
90
+ - Using the <kbd>↑</kbd> <kbd>↓</kbd> arrow keys in the input box, you can quickly switch between send records;
91
+ - Generating a new conversation every time is too cumbersome, try the `single-dialogue` function;
92
+ - The small button next to the answer bubble not only allows `one-click copy`, but also lets you `view the original Markdown text`;
93
+ - Specify the answer language, so that ChatGPT will always reply in a certain language.
94
+
95
+ ### 📜 Chat History
96
+ - Dialogue history will be automatically saved, you won't have to worry about not being able to find it after asking;
97
+ - Multi-user history isolation, only you can see them;
98
+ - Rename chat, easy to find in the future;
99
+ - <sup>New!</sup> Magically auto-name the chat, let LLM understand the conversation content, and automatically name the chat for you!
100
+ - <sup>New!</sup> Search chat, supports regular expressions!
101
+
102
+ ### 🖼️ Small and Beautiful Experience
103
+ - Self-developed Small-and-Beautiful theme, gives you a small and beautiful experience;
104
+ - Automatic light and dark color switching, gives you a comfortable experience from morning till night;
105
+ - Perfectly rendering LaTeX / tables / code blocks, supports code highlighting;
106
+ - <sup>New!</sup> Non-linear animations, frosted glass effect, so exquisite it doesn't look like Gradio!
107
+ - <sup>New!</sup> Adapted for Windows / macOS / Linux / iOS / Android, from icon to screen adaptation, gives you the most suitable experience!
108
+ - <sup>New!</sup> Supports PWA app installation for an even more native experience!
109
+
110
+ ### 👨‍💻 Geek Functions
111
+ - <sup>New!</sup> Supports Fine-tuning gpt-3.5!
112
+ - Plenty of available LLM parameters to adjust;
113
+ - Supports API-host switching;
114
+ - Supports custom proxies;
115
+ - Supports multiple api-key load balancing.
116
+
117
+ ### ⚒️ Deployment Related
118
+ - Deployment to the server: Set in `config.json` `"server_name": "0.0.0.0", "server_port": <your port number>,`.
119
+ - Obtain public link: Set in `config.json` `"share": true,`. Note that the program must be running to access it through public links.
120
+ - Use on Hugging Face: It's recommended to **Duplicate the Space** in the top right corner before using, the App response might be faster.
121
+
122
+ ## Quick Start
123
+
124
+ Execute the following commands in the terminal:
125
 
126
  ```shell
127
  git clone https://github.com/GaiZhenbiao/ChuanhuChatGPT.git
 
135
  python ChuanhuChatbot.py
136
  ```
137
 
138
+ A browser window will automatically open, at this point you can use **Chuanhu Chat** to chat with ChatGPT or other models.
139
 
140
  > **Note**
141
  >
142
+ > Please check our [wiki page](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程) for detailed instructions.).
143
+
144
 
145
  ## Troubleshooting
146
 
147
+ When you encounter problems, you should try to **manually pull the latest changes<sup>1</sup>** and **update dependencies<sup>2</sup>** first, then retry. Steps are:
148
 
149
+ 1. Click on the `Download ZIP` button on the website, download the latest code and unzip to replace, or
150
  ```shell
151
  git pull https://github.com/GaiZhenbiao/ChuanhuChatGPT.git main -f
152
  ```
153
+ 2. Try to install dependencies again (the project might have new dependencies)
154
  ```
155
  pip install -r requirements.txt
156
  ```
readme/README_ja.md CHANGED
@@ -1,6 +1,6 @@
1
  <div align="right">
2
  <!-- Language: -->
3
- <a title="Chinese" href="../README.md">简体中文</a> | <a title="English" href="README_en.md">English</a> | 日本語
4
  </div>
5
 
6
  <h1 align="center">川虎 Chat 🐯 Chuanhu Chat</h1>
@@ -22,11 +22,7 @@
22
  <img alt="GitHub pull requests" src="https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram" />
23
  </a>
24
  <p>
25
- ストリーム出力/会話回数無制限/履歴保存/プリセットプロンプト/ファイルへの質問チャット<br>
26
- ウェブ検索/LaTeXレンダリング/表レンダリング/コードハイライト<br>
27
- オートダークモード/アダプティブ・ウェブ・インターフェイス/WeChatライク・テーマ<br />
28
- マルチパラメーターチューニング/マルチAPI-Key対応/マルチユーザー対応<br>
29
- GPT-4対応/LLMのローカルデプロイ可能。
30
  </p>
31
  <a href="https://www.youtube.com/watch?v=MtxS4XZWbJE"><strong>動画チュートリアル</strong></a>
32
  ·
@@ -38,38 +34,90 @@
38
  ·
39
  <a href="https://huggingface.co/login?next=%2Fspaces%2FJohnSmith9982%2FChuanhuChatGPT%3Fduplicate%3Dtrue"><strong>ワンクリックデプロイ</strong></a>
40
  </p>
41
- <p align="center">
42
- <img alt="Animation Demo" src="https://user-images.githubusercontent.com/51039745/226255695-6b17ff1f-ea8d-464f-b69b-a7b6b68fffe8.gif" />
43
- </p>
44
  </p>
45
  </div>
46
 
47
- ## サポートされている大規模言語モデル
 
 
 
 
 
 
48
 
49
- **APIを通じてアクセス可能な大規模言語モデル**:
50
 
51
- - [ChatGPT](https://chat.openai.com) ([GPT-4](https://openai.com/product/gpt-4))
52
- - [Google PaLM](https://developers.generativeai.google/products/palm)
53
- - [Inspur Yuan 1.0](https://air.inspur.com/home)
54
- - [MiniMax](https://api.minimax.chat/)
55
- - [XMChat](https://github.com/MILVLG/xmchat)
56
 
57
- **ローカルに展開された大規模言語モデル**:
58
 
59
- - [ChatGLM](https://github.com/THUDM/ChatGLM-6B) ([ChatGLM2](https://github.com/THUDM/ChatGLM2-6B))
60
- - [LLaMA](https://github.com/facebookresearch/llama)
61
- - [StableLM](https://github.com/Stability-AI/StableLM)
62
- - [MOSS](https://github.com/OpenLMLab/MOSS)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  ## 使う上でのTips
65
 
66
- - ChatGPTをより適切に制御するために、システムプロンプトを使用できます。
67
- - プロンプトテンプレートを使用するには、プロンプトテンプレートコレクションを選択し、ドロップダウンメニューから特定のプロンプトを選択。回答が不十分な場合は、`🔄再生成`ボタンを使って再試行します。
68
- - 入力ボックスで改行するには、<kbd>Shift</kbd> + <kbd>Enter</kbd>キーを押してください。
69
- - 入力履歴を素早く切り替えるには、入力ボックスで <kbd>↑</kbd>と<kbd>↓</kbd>キーを押す。
70
- - プログラムをサーバーに展開するには、`config.json` 内の `"server_name": "0.0.0.0", "server_port": <ポート番号>`を設定してください。
71
- - 共有リンクを取得するには、 `config.json` 内の `"share": true` を設定してください。なお、公開リンクでアクセスするためには、プログラムが実行されている必要があることに注意してください。
72
- - Hugging Face Spacesで使用する場合: より速く、より安全に利用するために、**Duplicate Space**を使用し、自分のスペースでプログラムを実行することをお勧めします。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
  ## クイックスタート
75
 
 
1
  <div align="right">
2
  <!-- Language: -->
3
+ <a title="Chinese" href="../README.md">简体中文</a> | <a title="English" href="README_en.md">English</a> | 日本語 | <a title="Russian" href="README_ru.md">Russian</a>
4
  </div>
5
 
6
  <h1 align="center">川虎 Chat 🐯 Chuanhu Chat</h1>
 
22
  <img alt="GitHub pull requests" src="https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram" />
23
  </a>
24
  <p>
25
+ GPT-4対応 · ファイルへの質問チャット · LLMのローカルデプロイ可能 · ウェブ検索 · エージェントアシスタント · Fine-tuneをサポートします
 
 
 
 
26
  </p>
27
  <a href="https://www.youtube.com/watch?v=MtxS4XZWbJE"><strong>動画チュートリアル</strong></a>
28
  ·
 
34
  ·
35
  <a href="https://huggingface.co/login?next=%2Fspaces%2FJohnSmith9982%2FChuanhuChatGPT%3Fduplicate%3Dtrue"><strong>ワンクリックデプロイ</strong></a>
36
  </p>
 
 
 
37
  </p>
38
  </div>
39
 
40
+ [![Video Title](https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/51039745/0eee1598-c2fd-41c6-bda9-7b059a3ce6e7.jpg)](https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/51039745/0eee1598-c2fd-41c6-bda9-7b059a3ce6e7?autoplay=1)
41
+
42
+ ## ✨ 5.0の重要な更新!
43
+
44
+ ![ChuanhuChat5更新](https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/70903329/f2c2be3a-ea93-4edf-8221-94eddd4a0178)
45
+
46
+ <sup>新!</sup> 全く新しいユーザーインターフェース!Gradioに比べて精緻で、さらにフロストガラス効果があります!
47
 
48
+ <sup>新!</sup> モバイル端末(画面全体のスマホのパンチホール/ノッチを含む)に対応し、レイヤーがはっきりしてきました。
49
 
50
+ <sup>新!</sup> 履歴が左側に移動し、使いやすくなりました。また、検索(正規表現対応)、削除、リネームが可能です。
 
 
 
 
51
 
52
+ <sup>新!</sup> 大きなモデルによる履歴の自動命名が可能になりました(設定または設定ファイルで有効化が必要)。
53
 
54
+ <sup>新!</sup> 今では 川虎チャット を PWAアプリケーションとしてインストールすることも可能で、よりネイティブな体験ができます!Chrome/Edge/Safariなどのブラウザをサポート。
55
+
56
+ <sup>新!</sup> 各プラットフォームに適したアイコンで、見ていても気持ちがいい。
57
+
58
+ <sup>新!</sup> Finetune(微調整)GPT 3.5に対応!
59
+
60
+ ## モデルのサポート
61
+
62
+ | API呼び出しモデル | 備考 | ローカルデプロイモデル | 備考 |
63
+ | :---: | --- | :---: | --- |
64
+ | [ChatGPT(GPT-4)](https://chat.openai.com) | gpt-3.5の微調整をサポート | [ChatGLM](https://github.com/THUDM/ChatGLM-6B) ([ChatGLM2](https://github.com/THUDM/ChatGLM2-6B)) |
65
+ | [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service) | | [LLaMA](https://github.com/facebookresearch/llama) | Loraモデルのサポートあり 
66
+ | [Google PaLM](https://developers.generativeai.google/products/palm) | ストリーミング転送はサポートされていません | [StableLM](https://github.com/Stability-AI/StableLM)
67
+ | [讯飞星火认知大模型](https://xinghuo.xfyun.cn) | | [MOSS](https://github.com/OpenLMLab/MOSS)
68
+ | [Inspur Yuan 1.0](https://air.inspur.com/home) | | [Qwen](https://github.com/QwenLM/Qwen/tree/main)
69
+ | [MiniMax](https://api.minimax.chat/) |
70
+ | [XMChat](https://github.com/MILVLG/xmchat) | ストリーミング転送はサポー��されていません
71
+ | [Midjourney](https://www.midjourney.com/) | ストリーミング転送はサポートされていません
72
+ | [Claude](https://www.anthropic.com/) |
73
 
74
  ## 使う上でのTips
75
 
76
+ ### 💪 パワフルな機能
77
+ - **川虎助理**:AutoGPTに似ており、自動的に問題を解決します。
78
+ - **オンライン検索**:ChatGPTのデータが古い場合は、LLMにネットワークの翼を付けます。
79
+ - **ナレッジベース**:ChatGPTがあなたをクイックリーディングの世界へご招待!ファイルに基づいて質問に答えます。
80
+ - **LLMのローカルデプロイ**:ワンクリックであなた自身の大規模言語モデルをデプロイします。
81
+
82
+ ### 🤖 システムプロンプト
83
+ - システムプロンプトを使用して前提条件を設定すると、ロールプレイが効果的に行えます。
84
+ - 川虎Chatはプロンプトテンプレートを予め設定しており、「プロンプトテンプレートを読み込む」をクリックして、まずプロンプトテンプレートコレクションを選択し、次に下部で希望のプロンプトを選択します。
85
+
86
+ ### 💬 ベーシックな対話
87
+ - もし回答が満足できない場合、「再生成」ボタンを使用して再試行するか、直接「このラウンドの対話を削除」することができます。
88
+ - 入力ボックスは改行をサポートしており、 <kbd>Shift</kbd> + <kbd>Enter</kbd> を押すと改行できます。
89
+ - 入力ボックスで <kbd>↑</kbd> <kbd>↓</kbd> キーを押すと、送信履歴をスピーディに切り替えることができます。
90
+ - 各対話を新しく作成するのは面倒ですか?「単発対話」機能を試してみてください。
91
+ - 回答バブルの横の小さなボタンは「一括コピー」だけでなく、「Markdownの元のテキストを表示」もできます。
92
+ - 回答の言語を指定して、ChatGPTが特定の言語で回答するようにします。
93
+
94
+ ### 📜 履歴記録
95
+ - ダイアログの履歴は自動的に保存されるので、完了後に見つけることができます。
96
+ - 複数のユーザーの履歴は分離されており、他のユーザーは閲覧できません。
97
+ - 履歴の名前を変更することで、将来的な検索を容易にします。
98
+ - <sup>新!</sup> マジカルな自動履歴名付け機能で、LLMが対話内容を理解し、履歴に自動的に名前をつけてくれます!
99
+ - <sup>新!</sup> 正規表現をサポートする履歴検索!
100
+
101
+ ### 🖼️ シンプルな使いやすさ
102
+ - 独自のSmall-and-Beautifulテーマで、シンプルで美しい体験を提供します。
103
+ - 自動的な明暗の切り替えで、早朝から夜まで快適な体験ができます。
104
+ - LaTeX/テーブル/コードブロックを完璧にレンダリングし、コードハイライトがサポートされています。
105
+ - <sup>新!</sup> ノンリニアアニメーション、フロストガラスの効果など、Gradioのように洗練されています!
106
+ - <sup>新!</sup> Windows / macOS / Linux / iOS / Androidに対応し、アイコンからフルスクリーンまで、最適な体験を提供します!
107
+ - <sup>新!</sup> PWAアプリケーションのインストールがサポートされており、よりネイティブな体験ができます!
108
+
109
+ ### 👨‍💻 ギーク向け機能
110
+ - <sup>新!</sup> gpt-3.5のFine-tune(微調整)がサポートされています!
111
+ - 多くのLLMパラメータをカスタマイズできます。
112
+ - api-hostの変更が可能です。
113
+ - カスタムプロキシの設定が可能です。
114
+ - 負荷分散のための複数のapiキーのサポートがあります。
115
+
116
+ ### ⚒️ デプ���イに関する情報
117
+ - サーバーへのデプロイ:`config.json`ファイルで`"server_name": "0.0.0.0", "server_port": <あなたのポート番号>,"`を設定します。
118
+ - 共有リンクの取得:`config.json`ファイルで`"share": true,`を設定します。ただし、プログラムが実行されている必要があります。
119
+ - Hugging Faceでの使用:右上のコーナーの「Spaceをコピー」を選択し、それから使用することをおすすめします。これにより、アプリの反応が速くなる場合があります。
120
+
121
 
122
  ## クイックスタート
123
 
readme/README_ru.md ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div align="right">
2
+ <!-- Language: -->
3
+ <a title="Chinese" href="../README.md">简体中文</a> | <a title="English" href="README_en.md">English</a> | <a title="Japanese" href="README_ja.md">日本語</a> | Russian
4
+ </div>
5
+
6
+ <h1 align="center">川虎 Chat 🐯 Chuanhu Chat</h1>
7
+ <div align="center">
8
+ <a href="https://github.com/GaiZhenBiao/ChuanhuChatGPT">
9
+ <img src="https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/70903329/aca3a7ec-4f1d-4667-890c-a6f47bf08f63" alt="Logo" height="156">
10
+ </a>
11
+
12
+ <p align="center">
13
+ <h3>Легкий и удобный веб-интерфейс для LLM, включая ChatGPT/ChatGLM/LLaMA</h3>
14
+ <p align="center">
15
+ <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/blob/main/LICENSE">
16
+ <img alt="Tests Passing" src="https://img.shields.io/github/license/GaiZhenbiao/ChuanhuChatGPT" />
17
+ </a>
18
+ <a href="https://gradio.app/">
19
+ <img alt="GitHub Contributors" src="https://img.shields.io/badge/Base-Gradio-fb7d1a?style=flat" />
20
+ </a>
21
+ <a href="https://t.me/tkdifferent">
22
+ <img alt="GitHub pull requests" src="https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram" />
23
+ </a>
24
+ <p>
25
+ Поддержка GPT-4 · Анализ файлов в чате · Локальная установка LLM · Онлайн-поиск · Помощник Agent · Поддержка Fine-tune
26
+ </p>
27
+ <a href="https://www.youtube.com/watch?v=MtxS4XZWbJE"><strong>Видео туториал</strong></a>
28
+ ·
29
+ <a href="https://www.youtube.com/watch?v=77nw7iimYDE"><strong>2.0 Введение</strong></a>
30
+ ·
31
+ <a href="https://www.youtube.com/watch?v=x-O1jjBqgu4"><strong>3.0 Введение и руководство</strong></a>
32
+ ||
33
+ <a href="https://huggingface.co/spaces/JohnSmith9982/ChuanhuChatGPT"><strong>Пробная онлайн-версия</strong></a>
34
+ ·
35
+ <a href="https://huggingface.co/login?next=%2Fspaces%2FJohnSmith9982%2FChuanhuChatGPT%3Fduplicate%3Dtrue"><strong>Развертывание в один клик</strong></a>
36
+ </p>
37
+ </p>
38
+ </div>
39
+
40
+ [![Video Title](https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/51039745/0eee1598-c2fd-41c6-bda9-7b059a3ce6e7.jpg)](https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/51039745/0eee1598-c2fd-41c6-bda9-7b059a3ce6e7?autoplay=1)
41
+
42
+ ## ✨ Обновление 5.0!
43
+
44
+ ![ChuanhuChat5](https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/70903329/f2c2be3a-ea93-4edf-8221-94eddd4a0178)
45
+
46
+ <sup>New!</sup> Совершенно новый пользовательский интерфейс! Он такой приятный, не похожий на Gradio, с новым эффектом матового стекла!
47
+
48
+ <sup>New!</sup> Адаптация для мобильных устройств (включая экраны с отверстием/выемкой под камеру), иерархия стала более четкой.
49
+
50
+ <sup>New!</sup> История перенесена в левую часть для удобства использования. Поддерживается поиск (с поддержкой регулярных выражений), удаление и переименование.
51
+
52
+ <sup>New!</sup> Теперь можно автоматически давать истории имена для больших моделей (требуется включение в настройках или в конфигурационном файле).
53
+
54
+ <sup>New!</sup> Теперь можно установить Чуаньху Чат в качестве приложения PWA, чтобы повысить нативность! Поддерживаемые браузеры: Chrome/Edge/Safari и другие.
55
+
56
+ <sup>New!</sup> Значок адаптирован для различных платформ, выглядит более комфортно.
57
+
58
+ <sup>New!</sup> Поддержка Fine-tune (микронной настройки) GPT 3.5!
59
+
60
+ ## Поддерживаемые модели
61
+
62
+ | Модель с использованием API | Примечание | Локально развернутые модели | Примечание |
63
+ | :---: | --- | :---: | --- |
64
+ | [ChatGPT (GPT-4)](https://chat.openai.com) | Поддерживает микронастройку gpt-3.5 | [ChatGLM](https://github.com/THUDM/ChatGLM-6B) ([ChatGLM2](https://github.com/THUDM/ChatGLM2-6B)) |
65
+ | [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service) | | [LLaMA](https://github.com/facebookresearch/llama) | Поддерживает модель Lora 
66
+ | [Google PaLM](https://developers.generativeai.google/products/palm) | Не поддерживает потоковую передачу данных | [StableLM](https://github.com/Stability-AI/StableLM)
67
+ | [Xunfei Xinghuo Cognitive Model](https://xinghuo.xfyun.cn) | | [MOSS](https://github.com/OpenLMLab/MOSS)
68
+ | [Inspur Yuan 1.0](https://air.inspur.com/home) | | [Qwen](https://github.com/QwenLM/Qwen/tree/main)
69
+ | [MiniMax](https://api.minimax.chat/) |
70
+ | [XMChat](https://github.com/MILVLG/xmchat) | Не поддерживает потоковую передачу данных
71
+ | [Midjourney](https://www.midjourney.com/) | Не поддерживает потоковую передачу данных
72
+ | [Claude](https://www.anthropic.com/) |
73
+
74
+ ## Советы по использованию
75
+
76
+ ### 💪 Мощные функции
77
+ - **Chuanhu ассистент**: подобно AutoGPT, полностью автоматизированное решение вашей проблемы;
78
+ - **Поиск в Интернете**: данные ChatGPT устарели? Дайте LLM возможность использовать сеть;
79
+ - **База знаний**: позвольте ChatGPT помочь вам быстро прочитать информацию! Ответить на вопросы в соответствии с файлами.
80
+ - **Локальная установка LLM**: одним щелчком разверните свою собственную модель языка большого размера.
81
+
82
+ ### 🤖 Системный промт
83
+ - Установка предпосылок через системное сообщение позволяет эффективно играть роль персонажа;
84
+ - Чуаньху Чат предоставляет набор системных шаблонов, нажмите "Загрузить шаблон системного сообщения", затем выберите необходимый шаблон ниже.
85
+
86
+ ### 💬 Обычный диалог
87
+ - Если ответ не удовлетворяет вас, можно попробовать снова с помощью кнопки "Перегенерировать" или просто удалить этот раунд диалога;
88
+ - Поле ввода поддерживает перенос строки, нажмите <kbd>Shift</kbd> + <kbd>Enter</kbd>, чтобы сделать перенос строки;
89
+ - В поле ввода можно использовать клавиши <kbd>↑</kbd> и <kbd>↓</kbd>, чтобы быстро переключаться в истории отправки;
90
+ - Создание нового диалога слишком неудобно? Попробуйте функцию "Одиночный диалог";
91
+ - У кнопки возле пузыря с ответом можно не только "скопировать одним нажатием", но и "посмотреть исходный текст в формате Markdown";
92
+ - Укажите язык ответа, чтобы ChatGPT всегда отвечал на определенном языке.
93
+
94
+ ### 📜 История чатов
95
+ - История диалогов будет сохраняться автоматически, не нужно беспокоиться о том, что после вопросов они исчезнут;
96
+ - История диалогов защищена для каждого пользователя, никто кроме вас не может ее видеть;
97
+ - Переименуйте историю диалога, чтобы было удобнее искать в будущем;
98
+ - <sup>New!</sup> Магическое автоматическое именование истории диалога: позволяет LLM понять содержание диалога и автоматически называть историю диалога!
99
+ - <sup>New!</sup> Поиск истории диалога, поддержка регулярных выражений!
100
+
101
+ ### 🖼️ Красивый и компактный интерфейс
102
+ - Собственная тема Small-and-Beautiful принесет вам красивые и компактные впечатления;
103
+ - Автоматическое переключение светлой и темной темы обеспечит комфорт в любое время суток;
104
+ - Идеальное отображение LaTeX / таблиц / блоков кода, поддержка подсветки синтаксиса;
105
+ - <sup>New!</sup> Нелинейная анимация, эффект матового стекла – он такой изысканный, не похожий на Gradio!
106
+ - <sup>New!</sup> Поддержка Windows / macOS / Linux / iOS / Android, от иконки до адаптации под экраны с вырезами, предоставляет оптимальный опыт!
107
+ - <sup>New!</sup> Поддержка установки в качестве PWA-приложения, для более нативного опыта!
108
+
109
+ ### 👨‍💻 Технические возможности
110
+ - <sup>New!</sup> Поддержка Fine-tune (тонкой настройки) gpt-3.5!
111
+ - Множество настраиваемых параметров для LLM;
112
+ - Поддержка из��енения api-host;
113
+ - Поддержка настройки настраиваемого прокси-сервера;
114
+ - Поддержка балансировки нагрузки между несколькими ключами API.
115
+
116
+ ### ⚒️ Развертывание на сервере
117
+ - Развертывание на сервере: установите `"server_name": "0.0.0.0", "server_port": <порт>",` в `config.json`.
118
+ - Получение общедоступной ссылки: установите `"share": true` в `config.json`. Обратите внимание, что программа должна быть запущена, чтобы можно было получить доступ по общедоступной ссылке.
119
+ - Использование на Hugging Face: рекомендуется скопировать **Space** в правом верхнем углу, а затем использовать его, чтобы приложение было более отзывчивым.
120
+
121
+ ## Быстрый старт
122
+
123
+ ```shell
124
+ git clone https://github.com/GaiZhenbiao/ChuanhuChatGPT.git
125
+ cd ChuanhuChatGPT
126
+ pip install -r requirements.txt
127
+ ```
128
+
129
+ Затем создайте копию `config_example.json`, переименуйте ее в `config.json`, а затем укажите в файле свой API-ключ и другие настройки.
130
+
131
+ ```shell
132
+ python ChuanhuChatbot.py
133
+ ```
134
+
135
+ Откроется окно браузера, и вы сможете общаться с ChatGPT.
136
+
137
+ > **Примечание**
138
+ >
139
+ > Подробные инструкции см. на нашей [wiki-странице](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用教程).
140
+
141
+ ## Поиск и устранение неисправностей
142
+
143
+ При возникновении проблем следует сначала попробовать вручную подтянуть последние изменения этого проекта. Примерная инструкция:
144
+
145
+ 1. Загрузите архив с последней версией кода, нажав на кнопку `Download ZIP` на веб-странице, или
146
+ ```shell
147
+ git pull https://github.com/GaiZhenbiao/ChuanhuChatGPT.git main -f
148
+ ```
149
+ 2. Попробуйте установить зависимости еще раз (так как в этом проекте могли появиться новые зависимости)
150
+ ```
151
+ pip install -r requirements.txt
152
+ ```
153
+
154
+ Как правило, большинство проблем можно решить, выполнив следующие действия.
155
+
156
+ Если проблема сохраняется, обратитесь к этой странице: [Часто задаваемые вопросы (FAQ)](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/常见问题)
157
+
158
+ На этой странице перечислены практически все возможные проблемы и способы их решения. Пожалуйста, внимательно прочитайте его.
159
+
160
+ ## Дополнительная информация
161
+
162
+ Более подробную информацию можно найти в нашей [wiki](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki):
163
+
164
+ - [Как добавить перевод](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/Localization)
165
+ - [Как внести вклад](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/贡献指南)
166
+ - [Как цитировать проект](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用许可#如何引用该项目)
167
+ - [Журнал изменений проекта](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/更新日志)
168
+ - [Лицензия проекта](https://github.com/GaiZhenbiao/ChuanhuChatGPT/wiki/使用许可)
169
+
170
+ ## Starchart
171
+
172
+ [![Star History Chart](https://api.star-history.com/svg?repos=GaiZhenbiao/ChuanhuChatGPT&type=Date)](https://star-history.com/#GaiZhenbiao/ChuanhuChatGPT&Date)
173
+
174
+ ## Помощники
175
+
176
+ <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT/graphs/contributors">
177
+ <img src="https://contrib.rocks/image?repo=GaiZhenbiao/ChuanhuChatGPT" />
178
+ </a>
179
+
180
+ ## Спонсорство
181
+
182
+ 🐯 Если этот проект будет вам полезен, не стесняйтесь угостить меня колой или чашкой кофе~.
183
+
184
+ <a href="https://www.buymeacoffee.com/ChuanhuChat" ><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=ChuanhuChat&button_colour=219d53&font_colour=ffffff&font_family=Poppins&outline_colour=ffffff&coffee_colour=FFDD00" alt="Buy Me A Coffee" width="250"></a>
185
+
186
+ <img width="250" alt="image" src="https://user-images.githubusercontent.com/51039745/226920291-e8ec0b0a-400f-4c20-ac13-dafac0c3aeeb.JPG">
requirements.txt CHANGED
@@ -1,5 +1,5 @@
1
- gradio==3.40.0
2
- gradio_client==0.4.0
3
  pypinyin
4
  tiktoken
5
  socksio
@@ -7,7 +7,7 @@ tqdm
7
  colorama
8
  googlesearch-python
9
  Pygments
10
- langchain==0.0.276
11
  markdown
12
  PyPDF2
13
  pdfplumber
@@ -17,12 +17,17 @@ openpyxl
17
  pandoc
18
  wolframalpha
19
  faiss-cpu==1.7.4
20
- duckduckgo-search
21
  arxiv
22
  wikipedia
23
  google.generativeai
24
- openai>=0.27.9
25
  unstructured
26
  google-api-python-client
27
  tabulate
28
  ujson
 
 
 
 
 
 
1
+ gradio==3.43.2
2
+ gradio_client==0.5.0
3
  pypinyin
4
  tiktoken
5
  socksio
 
7
  colorama
8
  googlesearch-python
9
  Pygments
10
+ langchain==0.0.316
11
  markdown
12
  PyPDF2
13
  pdfplumber
 
17
  pandoc
18
  wolframalpha
19
  faiss-cpu==1.7.4
20
+ duckduckgo-search>=3.9.5
21
  arxiv
22
  wikipedia
23
  google.generativeai
24
+ openai==0.28.1
25
  unstructured
26
  google-api-python-client
27
  tabulate
28
  ujson
29
+ python-docx
30
+ websocket_client
31
+ pydantic==1.10.8
32
+ google-search-results
33
+ anthropic==0.3.11
requirements_advanced.txt CHANGED
@@ -1,11 +1,12 @@
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
 
 
 
 
 
1
  transformers
2
  huggingface_hub
3
  torch
 
 
 
4
  cpm-kernels
5
  sentence_transformers
6
  accelerate
7
  sentencepiece
8
+ llama-cpp-python
9
+ transformers_stream_generator
10
+ einops
11
+ optimum
12
+ auto-gptq
templates/6 Russian Prompts.json ADDED
The diff for this file is too large to render. See raw diff
 
web_assets/.DS_Store ADDED
Binary file (6.15 kB). View file
 
web_assets/html/chatbot_header_btn.html ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="header-btn-groups">
2
+ <div class="btn-bar-group" style="margin-left: -12px;">
3
+ <span class="show-on-gpt">
4
+ <button id="chuanhu-training-btn" onclick="openTrainingBox()" class="chuanhu-ui-btn">
5
+ <!-- <svg width="24px" height="24px" viewBox="0 0 45.297 28.8394" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
6
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
7
+ <g fill="currentColor" fill-rule="nonzero">
8
+ <path d="M0,23.4288 C0,27.0248 1.8307,28.8394 5.45907,28.8394 L39.8379,28.8394 C43.4663,28.8394 45.297,27.0248 45.297,23.4288 L45.297,5.41055 C45.297,1.81453 43.4663,0 39.8379,0 L9.91219,0 C6.82641,0 5.13938,1.74422 4.24945,4.43907 L0.618281,15.4643 C0.1875,16.7405 0,17.8041 0,19.4644 L0,23.4288 Z M17.6306,14.2226 L17.6306,8.14923 C17.6306,7.09126 18.2522,6.46969 19.3263,6.46969 L36.3459,6.46969 C37.4201,6.46969 38.0344,7.09126 38.0344,8.14923 L38.0344,14.2226 C38.0344,15.2498 37.4201,15.8714 36.3459,15.8714 L19.3263,15.8714 C18.2522,15.8714 17.6306,15.2498 17.6306,14.2226 Z M6.14415,15.8714 C5.12415,15.8714 4.63102,15.0713 5.01259,13.8724 L7.02539,7.75805 C7.27758,6.97805 7.66875,6.46969 8.95946,6.46969 L11.2486,6.46969 C12.3066,6.46969 12.9443,7.09126 12.9443,8.14923 L12.9443,14.2226 C12.9443,15.2498 12.3066,15.8714 11.2486,15.8714 L6.14415,15.8714 Z" fill-opacity="0.85"></path>
9
+ </g>
10
+ </g>
11
+ </svg> -->
12
+ <svg width="24px" height="24px" viewBox="0 0 21.4889 22.038" version="1.1" xmlns="http://www.w3.org/2000/svg">
13
+ <g transform-origin="center" transform="scale(0.85)">
14
+ <path d="M1.1492 20.81C2.69607 22.3569 4.95779 22.3803 6.51638 20.5991C8.18045 18.7006 10.0672 15.1381 12.6922 12.5131C14.6961 10.5092 17.1101 12.3959 19.8875 9.75923C21.0711 8.63423 21.7039 7.02876 21.4226 5.88032L17.8015 6.87642C17.2976 7.00532 16.8992 6.66548 16.7234 6.05611L16.4539 5.14204C16.2781 4.53267 16.5125 4.04048 17.0164 3.89986L20.6258 2.9272C20.532 2.57564 20.1922 2.09517 19.782 1.67329C17.6258-0.553269 14.0281-0.494675 11.9539 1.47407C8.86013 4.40376 11.1687 7.49751 9.23513 9.43111C6.80935 11.8569 3.24685 13.7788 1.34842 15.4428C-0.432835 17.0014-0.397679 19.2631 1.1492 20.81ZM2.13357 19.8256C1.17263 18.8413 1.17263 17.3881 2.30935 16.4038C4.19607 14.7514 8.00467 12.6303 10.2195 10.4155C12.5867 8.04829 10.0086 5.16548 12.9617 2.45845C14.4617 1.06392 17.1804 1.00532 18.8914 2.59907L19.2429 1.9897L16.7351 2.64595C15.3523 3.00923 14.7429 4.07564 15.1531 5.51704L15.4109 6.40767C15.8797 8.01314 16.8054 8.45845 18.0594 8.11861L20.6609 7.41548L20.0398 7.09907C19.8406 7.75532 19.4773 8.3647 18.9617 8.86861C16.7351 11.0483 14.075 9.17329 11.7078 11.5288C9.30545 13.9194 7.20779 17.7514 5.55545 19.6499C4.54763 20.81 3.09451 20.81 2.13357 19.8256ZM13.0086 9.09126L15.0125 8.5522C15.1883 8.50532 15.2937 8.31782 15.2469 8.11861C15.2 7.94282 15.0242 7.82564 14.825 7.88423L12.8328 8.42329C12.657 8.47017 12.5515 8.65767 12.5867 8.84517C12.6219 9.04439 12.8211 9.13814 13.0086 9.09126ZM12.7625 8.08345L14.7429 7.55611C14.9304 7.50923 15.0359 7.32173 14.989 7.12251C14.9422 6.93501 14.7429 6.82954 14.5672 6.87642L12.575 7.41548C12.3875 7.46236 12.282 7.66157 12.3289 7.82564C12.3875 8.03657 12.5633 8.14204 12.7625 8.08345ZM12.5164 7.07564L14.4969 6.54829C14.6844 6.50142 14.7898 6.31392 14.7429 6.1147C14.6961 5.9272 14.4969 5.82173 14.3211 5.86861L12.3289 6.40767C12.1414 6.45454 12.0359 6.65376 12.0828 6.81782C12.1414 7.02876 12.3172 7.13423 12.5164 7.07564ZM16.5242 10.5444L17.4265 10.3452L16.3133 6.22017L15.4109 6.40767ZM3.80935 19.3217C4.44217 19.3217 4.96951 18.8061 4.96951 18.1616C4.96951 17.5288 4.44217 17.0014 3.80935 17.0014C3.16482 17.0014 2.63748 17.5288 2.63748 18.1616C2.63748 18.8061 3.16482 19.3217 3.80935 19.3217Z" fill="currentColor" fill-opacity="0.85"/>
15
+ </g>
16
+ </svg>
17
+ </button>
18
+ </span>
19
+ </div>
20
+
21
+ <div class="btn-bar-group">
22
+ <button id="new-chat-btn" onclick="newChatClick()" class="chuanhu-ui-btn">
23
+ <!-- <svg width="24px" height="24px" viewBox="0 0 41.3058 37.9805" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
24
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
25
+ <g fill-rule="nonzero">
26
+ <path d="M7.85906,37.9732 C10.2516,37.9732 15.1013,35.6613 18.7802,33.1193 C31.3151,33.6708 41.3058,26.4138 41.3058,16.6013 C41.3058,7.42008 32.1359,0 20.6529,0 C9.17883,0 0,7.42008 0,16.6013 C0,22.5973 3.85266,27.9152 9.65789,30.6202 C8.82141,32.2172 7.33664,34.2469 6.52195,35.292 C5.57226,36.5116 6.08883,37.9732 7.85906,37.9732 Z M9.48751,35.2875 C9.34688,35.336 9.30727,35.2334 9.40829,35.1089 C10.392,33.8965 11.858,32.0149 12.5182,30.7552 C12.9734,29.9184 12.8473,29.1593 11.8221,28.6863 C6.06376,26.0217 2.68407,21.6335 2.68407,16.6013 C2.68407,8.91657 10.6542,2.66954 20.6529,2.66954 C30.6677,2.66954 38.6217,8.91657 38.6217,16.6013 C38.6217,24.2698 30.6677,30.5241 20.6529,30.5241 C20.1642,30.5241 19.5356,30.495 18.747,30.4659 C18.0687,30.4587 17.5069,30.66 16.9006,31.1402 C14.4764,32.7921 11.1403,34.7288 9.48751,35.2875 Z" fill-opacity="0.85" fill="currentColor"></path>
27
+ <path d="M12.233,16.7468 C12.233,17.5238 12.7908,18.0816 13.6235,18.0816 L19.2947,18.0816 L19.2947,23.7778 C19.2947,24.5855 19.8525,25.1522 20.6295,25.1522 C21.4371,25.1522 22.0111,24.5927 22.0111,23.7778 L22.0111,18.0816 L27.7146,18.0816 C28.515,18.0816 29.0817,17.5238 29.0817,16.7468 C29.0817,15.9391 28.5223,15.3652 27.7146,15.3652 L22.0111,15.3652 L22.0111,9.67782 C22.0111,8.85399 21.4371,8.28727 20.6295,8.28727 C19.8525,8.28727 19.2947,8.86125 19.2947,9.67782 L19.2947,15.3652 L13.6235,15.3652 C12.7835,15.3652 12.233,15.9391 12.233,16.7468 Z" fill-opacity="0.85" fill="currentColor"></path>
28
+ </g>
29
+ </g>
30
+ </svg> -->
31
+ <svg width="24px" height="24px" viewBox="0 0 15.0076 15.0274" version="1.1" xmlns="http://www.w3.org/2000/svg">
32
+ <g transform-origin="center" transform="scale(0.75)">
33
+ <path d="M0 7.50785C0 7.89996 0.328243 8.23184 0.723986 8.23184L6.78387 8.23184L6.78387 14.2917C6.78387 14.6794 7.11574 15.0157 7.50785 15.0157C7.89996 15.0157 8.22375 14.6794 8.22375 14.2917L8.22375 8.23184L14.2917 8.23184C14.6794 8.23184 15.0076 7.89996 15.0076 7.50785C15.0076 7.11574 14.6794 6.78387 14.2917 6.78387L8.22375 6.78387L8.22375 0.723986C8.22375 0.336329 7.89996 0 7.50785 0C7.11574 0 6.78387 0.336329 6.78387 0.723986L6.78387 6.78387L0.723986 6.78387C0.328243 6.78387 0 7.11574 0 7.50785Z" fill="currentColor" fill-opacity="0.85"/>
34
+ </g>
35
+ </svg>
36
+ </button>
37
+
38
+ <div class="nav-item-dropdown">
39
+ <button id="export-chat-btn" onclick="" class="chuanhu-ui-btn dropdown-trigger">
40
+ <svg width="24px" height="24px" viewBox="0 0 16.4883 25.0898" version="1.1" xmlns="http://www.w3.org/2000/svg">
41
+ <g stroke="none" fill="currentColor" fill-rule="nonzero" transform-origin="center" transform="scale(1.1)">
42
+ <path d="M13.5,7.39453 C15.5039,7.39453 16.4883,8.37891 16.4883,10.3477 L16.4883,19.3594 C16.4883,21.3281 15.5039,22.3125 13.5,22.3125 L2.98828,22.3125 C0.996094,22.3125 0,21.3281 0,19.3594 L0,10.3477 C0,8.37891 0.996094,7.39453 2.98828,7.39453 L5.554,7.394 L5.554,9.093 L3.01172,9.09375 C2.21760235,9.09375 1.74528291,9.49859318 1.69246372,10.2789831 L1.6875,10.4297 L1.6875,19.2773 C1.6875,20.168 2.16797,20.6133 3.01172,20.6133 L13.4766,20.6133 C14.3086,20.6133 14.8008,20.168 14.8008,19.2773 L14.8008,10.4297 C14.8008,9.55078 14.3086,9.09375 13.4766,9.09375 L10.921,9.093 L10.921,7.394 Z" fill-opacity="0.85"></path>
43
+ <path d="M8.23828,15.0469 C8.69531,15.0469 9.08203,14.6719 9.08203,14.2266 L9.08203,5.08594 L9.01172,3.67969 L9.50391,4.21875 L10.793,5.61328 C10.9453,5.77734 11.1562,5.85938 11.3555,5.85938 C11.8008,5.85938 12.1289,5.55469 12.1289,5.13281 C12.1289,4.89844 12.0352,4.73438 11.8711,4.58203 L8.84766,1.6875 C8.63672,1.47656 8.46094,1.40625 8.23828,1.40625 C8.02734,1.40625 7.85156,1.47656 7.62891,1.6875 L4.60547,4.58203 C4.45312,4.73438 4.35938,4.89844 4.35938,5.13281 C4.35938,5.55469 4.67578,5.85938 5.12109,5.85938 C5.32031,5.85938 5.54297,5.77734 5.69531,5.61328 L6.98438,4.21875 L7.47656,3.67969 L7.40625,5.08594 L7.40625,14.2266 C7.40625,14.6719 7.79297,15.0469 8.23828,15.0469 Z" fill-opacity="0.85"></path>
44
+ </g>
45
+ </svg>
46
+ </button>
47
+ <div class="dropdown-menu">
48
+ <div class="dropdown-menu-item">
49
+ <button onclick="jsonDownloadClick()">{json_label}</button>
50
+ </div>
51
+ <div class="dropdown-menu-item">
52
+ <button onclick="mdDownloadClick()">{md_label}</button>
53
+ </div>
54
+ </div>
55
+ </div>
56
+
57
+ <button id="open-toolbox-btn" onclick="toolboxClick()" class="chuanhu-ui-btn">
58
+ <!-- <svg width="24px" height="24px" viewBox="0 0 33.5163 33.5705" version="1.1" xmlns="http://www.w3.org/2000/svg">
59
+ <g stroke="none" fill="currentColor" fill-rule="nonzero" transform-origin="center" transform="scale(0.8)">
60
+ <path d="M5.58422,33.5705 L27.9321,33.5705 C31.6493,33.5705 33.5163,31.6889 33.5163,28.0348 L33.5163,5.57367 C33.5163,1.91953 31.6493,0.0379686 27.9321,0.0379686 L5.58422,0.0379686 C1.8832,0.0379686 0,1.905 0,5.57367 L0,28.0348 C0,31.7034 1.8832,33.5705 5.58422,33.5705 Z M5.6311,30.9495 C3.67477,30.9495 2.60485,29.928 2.60485,27.907 L2.60485,5.70141 C2.60485,3.69657 3.67477,2.65899 5.6311,2.65899 L27.8852,2.65899 C29.8125,2.65899 30.9115,3.69657 30.9115,5.70141 L30.9115,27.907 C30.9115,29.928 29.8125,30.9495 27.8852,30.9495 L5.6311,30.9495 Z" fill-opacity="0.85"></path>
61
+ <path d="M7.69266,12.7402 L17.3438,12.7402 L17.3438,10.572 L7.69266,10.572 C7.07907,10.572 6.60047,11.0505 6.60047,11.648 C6.60047,12.2616 7.07907,12.7402 7.69266,12.7402 Z M20.021,15.3488 C22.0545,15.3488 23.704,13.683 23.704,11.6334 C23.704,9.6 22.0545,7.9343 20.021,7.9343 C17.9876,7.9343 16.3219,9.6 16.3219,11.6334 C16.3219,13.683 17.9876,15.3488 20.021,15.3488 Z M20.021,13.5841 C18.922,13.5841 18.0865,12.7179 18.0865,11.6262 C18.0865,10.5345 18.922,9.69165 20.021,9.69165 C21.0966,9.69165 21.9483,10.5345 21.9483,11.6262 C21.9483,12.7179 21.0966,13.5841 20.021,13.5841 Z M22.5487,12.7402 L25.8884,12.7402 C26.4534,12.7402 26.932,12.2616 26.932,11.648 C26.932,11.0505 26.4534,10.572 25.8884,10.572 L22.5487,10.572 L22.5487,12.7402 Z M25.8237,20.5158 L16.1726,20.5158 L16.1726,22.6913 L25.8237,22.6913 C26.4534,22.6913 26.932,22.2054 26.932,21.608 C26.932,21.0016 26.4534,20.5158 25.8237,20.5158 Z M13.4953,17.9145 C11.4781,17.9145 9.81962,19.5802 9.81962,21.6225 C9.81962,23.6559 11.4781,25.3216 13.4953,25.3216 C15.5288,25.3216 17.1945,23.6559 17.1945,21.6225 C17.1945,19.5802 15.5288,17.9145 13.4953,17.9145 Z M13.4953,19.6791 C14.5943,19.6791 15.4298,20.538 15.4298,21.6298 C15.4298,22.7288 14.5943,23.5643 13.4953,23.5643 C12.4198,23.5643 11.5842,22.7288 11.5842,21.6298 C11.5842,20.538 12.4198,19.6791 13.4953,19.6791 Z M10.9838,20.5158 L7.64415,20.5158 C7.07907,20.5158 6.60047,21.0016 6.60047,21.608 C6.60047,22.2054 7.07907,22.6913 7.64415,22.6913 L10.9838,22.6913 L10.9838,20.5158 Z" fill-opacity="0.85" transform="translate(16.7662, 16.628) scale(-1, 1) translate(-16.7662, -16.628)"></path>
62
+ </g>
63
+ </svg> -->
64
+ <svg width="24px" height="24px" viewBox="0 0 17.0977 17.0977" viewBox="0 0 17.0742 17.0977" version="1.1" xmlns="http://www.w3.org/2000/svg">
65
+ <g transform-origin="center" transform="scale(0.82)" fill="currentColor" fill-rule="nonzero" fill-opacity="0.82">
66
+ <path d="M2.98828,17.0977 L14.0859,17.0977 C16.0898,17.0977 17.0742,16.1133 17.0742,14.1445 L17.0742,2.96484 C17.0742,0.996094 16.0898,0.0117188 14.0859,0.0117188 L2.98828,0.0117188 C0.996094,0.0117188 0,0.996094 0,2.96484 L0,14.1445 C0,16.1133 0.996094,17.0977 2.98828,17.0977 Z M3.01172,15.3984 C2.16797,15.3984 1.6875,14.9531 1.6875,14.0625 L1.6875,3.04688 C1.6875,2.16797 2.16797,1.71094 3.01172,1.71094 L14.0625,1.71094 C14.9062,1.71094 15.3867,2.16797 15.3867,3.04688 L15.3867,14.0625 C15.3867,14.9531 14.9062,15.3984 14.0625,15.3984 L3.01172,15.3984 Z"></path>
67
+ <path d="M4.34766,6.66797 L8.67188,6.66797 L8.67188,5.29688 L4.34766,5.29688 C3.96094,5.29688 3.65625,5.60156 3.65625,5.97656 C3.65625,6.36328 3.96094,6.66797 4.34766,6.66797 Z M9.89062,7.86328 C10.9219,7.86328 11.7539,7.01953 11.7539,5.97656 C11.7539,4.94531 10.9219,4.10156 9.89062,4.10156 C8.85938,4.10156 8.01562,4.94531 8.01562,5.97656 C8.01562,7.01953 8.85938,7.86328 9.89062,7.86328 Z M9.89062,6.83203 C9.41016,6.83203 9.04688,6.45703 9.04688,5.97656 C9.04688,5.49609 9.41016,5.13281 9.89062,5.13281 C10.3594,5.13281 10.7344,5.49609 10.7344,5.97656 C10.7344,6.45703 10.3594,6.83203 9.89062,6.83203 Z M11.0273,6.66797 L12.7734,6.66797 C13.125,6.66797 13.4297,6.36328 13.4297,5.97656 C13.4297,5.60156 13.125,5.29688 12.7734,5.29688 L11.0273,5.29688 L11.0273,6.66797 Z M12.7266,10.207 L8.40234,10.207 L8.40234,11.5781 L12.7266,11.5781 C13.125,11.5781 13.4297,11.2734 13.4297,10.8984 C13.4297,10.5117 13.125,10.207 12.7266,10.207 Z M7.18359,9.01172 C6.16406,9.01172 5.32031,9.85547 5.32031,10.8984 C5.32031,11.9297 6.16406,12.7734 7.18359,12.7734 C8.21484,12.7734 9.05859,11.9297 9.05859,10.8984 C9.05859,9.85547 8.21484,9.01172 7.18359,9.01172 Z M7.18359,10.043 C7.66406,10.043 8.02734,10.418 8.02734,10.8984 C8.02734,11.3789 7.66406,11.7422 7.18359,11.7422 C6.71484,11.7422 6.35156,11.3789 6.35156,10.8984 C6.35156,10.418 6.71484,10.043 7.18359,10.043 Z M6.05859,10.207 L4.3125,10.207 C3.96094,10.207 3.65625,10.5117 3.65625,10.8984 C3.65625,11.2734 3.96094,11.5781 4.3125,11.5781 L6.05859,11.5781 L6.05859,10.207 Z" transform="translate(8.543, 8.4375) scale(-1, 1) translate(-8.543, -8.4375)"></path>
68
+ </g>
69
+ </svg>
70
+ </button>
71
+ </div>
72
+ </div>
web_assets/html/chatbot_more.html ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div>
2
+ <div id="chatbot-input-more-area">
3
+ <span class="chatbot-input-more-label-group">
4
+ <div class="switch-checkbox">
5
+ <label>
6
+ <input type="checkbox" name="single-session-cb" data-testid="checkbox" style="transform: scale(0.8); margin: 0;">
7
+ <span class="chatbot-input-more-span">{single_turn_label}</span>
8
+ </label>
9
+ </div>
10
+
11
+ <div class="switch-checkbox">
12
+ <label>
13
+ <input type="checkbox" name="online-search-cb" data-testid="checkbox" style="transform: scale(0.8); margin: 0;">
14
+ <span class="chatbot-input-more-span">{websearch_label}</span>
15
+ </label>
16
+ </div>
17
+ </span>
18
+
19
+ <span class="chatbot-input-more-label-group">
20
+ <div class="chatbot-input-more-btn last-btn">
21
+ <label class="may-disable-label">
22
+ <div id="uploaded-files-div">
23
+ <span class="chatbot-input-more-span tooltip-toggle" aria-label="{uploaded_files_tip}">{uploaded_files_label}</span>
24
+ <span class="chatbot-input-more-icon" id="uploaded-files-count"></span>
25
+ </div>
26
+ <button id="upload-files-btn">
27
+ <span class="chatbot-input-more-span">{upload_file_label}</span>
28
+ <span class="chatbot-input-more-icon">
29
+ <svg width="18px" height="22px" viewBox="0 0 17.6625708 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
30
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
31
+ <g fill="currentColor" fill-rule="nonzero">
32
+ <path d="M10.6020285,2.51882667 C11.4362625,2.51882667 11.8921676,2.64807396 12.4438001,3.21682942 L16.9677143,7.80362991 C17.5473088,8.39718377 17.6625708,8.80801259 17.6625708,9.74013504 L17.6625708,17.0701838 C17.6625708,18.6697194 16.8588053,19.4835978 15.2701606,19.4835978 L9.30367012,19.4838726 C9.50801444,19.132339 9.67081559,18.754537 9.78494219,18.3575337 L15.2149282,18.3578162 C16.0814028,18.3578162 16.5329428,17.8954285 16.5329428,17.0499579 L16.5329428,9.75491554 L11.9107944,9.75491554 C10.9798389,9.75491554 10.4963609,9.29343541 10.4963609,8.34048196 L10.4963609,3.64458667 L6.87556994,3.64458667 C6.00213732,3.64458667 5.55375219,4.12267974 5.55375219,4.95246234 L5.55332282,12.0499339 C5.36755588,12.0285691 5.17882362,12.0175857 4.9877281,12.0175857 C4.79846417,12.0175857 4.61149369,12.028399 4.42740668,12.0494356 L4.42797059,4.93219317 C4.42797059,3.3327051 5.23415624,2.51882667 6.81337946,2.51882667 Z M11.552216,3.86783276 L11.552216,8.21151991 C11.552216,8.55164434 11.6926308,8.69205911 12.0359101,8.69205911 L16.3097226,8.69205911 L11.552216,3.86783276 Z" fill-opacity="0.85"></path>
33
+ <path d="M4.9877281,13.0174305 C7.17286548,13.0174305 8.97241326,14.8169783 8.97241326,17.0052706 C8.97241326,19.1904512 7.15190483,20.9970003 4.9877281,20.9970003 C2.80254317,20.9970003 0.999853452,19.1974525 0.999853452,17.0052706 C0.999853452,14.82009 2.80254317,13.0174305 4.9877281,13.0174305 Z M4.99784107,14.437007 C4.88115289,14.437007 4.7753124,14.4852382 4.64150995,14.6120393 L2.60482154,16.530609 C2.48641329,16.6350666 2.4357663,16.7447535 2.42877798,16.8911323 C2.414797,17.1440431 2.62022006,17.325342 2.87314384,17.325342 C3.00310422,17.3323433 3.12991834,17.2638862 3.21236934,17.1782716 L3.90166369,16.4757224 L4.5558954,15.8144894 L4.51466558,16.8528413 L4.51466558,19.0792085 C4.51466558,19.332854 4.73408257,19.5421148 4.99784107,19.5421148 C5.25844467,19.5421148 5.47470676,19.332854 5.47470676,19.0792085 L5.47470676,16.8528413 L5.44363313,15.8144894 L6.08075058,16.4757224 L6.78018815,17.1782716 C6.86960587,17.2638862 6.98560256,17.3183839 7.11555862,17.325342 C7.36531454,17.3393445 7.56061597,17.1440431 7.56061597,16.8911323 C7.56061597,16.7479084 7.50996466,16.6350666 7.39159099,16.530609 L5.34090431,14.6120393 C5.22352465,14.498506 5.12779713,14.437007 4.99784107,14.437007 Z" fill-opacity="0.85"></path>
34
+ </g>
35
+ </g>
36
+ </svg>
37
+ </span>
38
+ </button>
39
+ </label>
40
+ </div>
41
+ </span>
42
+ </div>
43
+
44
+ <!-- get more button -->
45
+ <div id="chatbot-input-more-btn-div">
46
+ <button class="chatbot-input-more-btn" onclick="chatMoreBtnClick()">
47
+ <!-- <svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg"
48
+ xmlns:xlink="http://www.w3.org/1999/xlink">
49
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
50
+ <g fill="currentColor" fill-rule="nonzero">
51
+ <path
52
+ d="M15.9930223,31.97976 C24.7467023,31.97976 32,24.7418401 32,15.98988 C32,7.23796297 24.7327468,0 15.9790668,0 C7.23313125,0 0,7.23796297 0,15.98988 C0,24.7418401 7.24706085,31.97976 15.9930223,31.97976 Z"
53
+ fill-opacity="0.1" class="sm-round-bg"></path>
54
+ <path
55
+ d="M8.41162523,16.0038327 C8.41162523,15.2803594 8.92769229,14.7720332 9.65130778,14.7720332 L14.7951802,14.7720332 L14.7951802,9.62925761 C14.7951802,8.91198549 15.2756953,8.40371964 15.9728644,8.40371964 C16.6964799,8.40371964 17.2048198,8.90578429 17.2048198,9.62925761 L17.2048198,14.7720332 L22.3626477,14.7720332 C23.0723077,14.7720332 23.5884006,15.2803594 23.5884006,16.0038327 C23.5884006,16.7008648 23.0660191,17.1952382 22.3626477,17.1952382 L17.2048198,17.1952382 L17.2048198,22.3380138 C17.2048198,23.0614872 16.6964799,23.563526 15.9728644,23.563526 C15.2756953,23.563526 14.7951802,23.0413333 14.7951802,22.3380138 L14.7951802,17.1952382 L9.65130778,17.1952382 C8.93398085,17.1952382 8.41162523,16.7008648 8.41162523,16.0038327 Z"
56
+ fill-opacity="0.85"></path>
57
+ </g>
58
+ </g>
59
+ </svg> -->
60
+ <svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
61
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
62
+ <g fill="currentColor" fill-rule="nonzero">
63
+ <path d="M15.9930223,31.97976 C24.7467023,31.97976 32,24.7418401 32,15.98988 C32,7.23796297 24.7327468,0 15.9790668,0 C7.23313125,0 0,7.23796297 0,15.98988 C0,24.7418401 7.24706085,31.97976 15.9930223,31.97976 Z" fill-opacity="0.1" class="sm-round-bg"></path>
64
+ <path d="M23.5318035,18.2475474 C22.2777951,18.2475474 21.2612876,17.2374408 21.2612876,15.9773915 C21.2612876,14.7173421 22.2777951,13.7072355 23.5318035,13.7072355 C24.7781451,13.7072355 25.8086942,14.7173421 25.8086942,15.9773915 C25.8086942,17.2374408 24.7781451,18.2475474 23.5318035,18.2475474 Z" fill-opacity="0.75"></path>
65
+ <path d="M15.9930223,18.2475474 C14.7327253,18.2475474 13.7224202,17.2374408 13.7224202,15.9773915 C13.7224202,14.7173421 14.7327253,13.7072355 15.9930223,13.7072355 C17.2533193,13.7072355 18.2775798,14.7173421 18.2775798,15.9773915 C18.2775798,17.2374408 17.2533193,18.2475474 15.9930223,18.2475474 Z" fill-opacity="0.75"></path>
66
+ <path d="M8.468162,18.2475474 C7.22182045,18.2475474 6.19131446,17.2374408 6.19131446,15.9773915 C6.19131446,14.7173421 7.22182045,13.7072355 8.468162,13.7072355 C9.70824943,13.7072355 10.7387124,14.7173421 10.7387124,15.9773915 C10.7387124,17.2374408 9.72220487,18.2475474 8.468162,18.2475474 Z" fill-opacity="0.75"></path>
67
+ </g>
68
+ </g>
69
+ </svg>
70
+ </button>
71
+ </div>
72
+ </div>
web_assets/html/close_btn.html ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ <button onclick='closeBtnClick("{obj}")'>
2
+ <svg class="icon-need-hover" stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round"
3
+ stroke-linejoin="round" height="20" width="20" xmlns="http://www.w3.org/2000/svg"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line>
4
+ </svg>
5
+ </button>
web_assets/html/func_nav.html ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="menu-footer-btn-bar" class="is-gpt">
2
+ <div class="btn-bar-group">
3
+ <button id="chuanhu-setting-btn" onclick="openSettingBox()" class="chuanhu-ui-btn">
4
+ <!-- <svg width="24px" height="24px" viewBox="0 0 39.6797 39.6328" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
5
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
6
+ <g id="gearshape" fill-rule="nonzero">
7
+ <path d="M17.9766,39.6094 L21.6797,39.6094 C23.1797,39.6094 24.3516,38.6953 24.7031,37.2891 L25.4766,33.9141 L25.8984,33.7734 L28.8047,35.5781 C30.0703,36.3516 31.5469,36.1641 32.6016,35.0859 L35.1797,32.5312 C36.2344,31.4766 36.4453,29.9766 35.6484,28.7578 L33.8203,25.8281 L33.9609,25.4766 L37.3359,24.7031 C38.7422,24.3516 39.6797,23.1562 39.6797,21.6797 L39.6797,18.0703 C39.6797,16.5938 38.7656,15.3984 37.3359,15.0469 L34.0078,14.2266 L33.8438,13.8516 L35.6719,10.9219 C36.4688,9.67969 36.2578,8.22656 35.2031,7.125 L32.625,4.57031 C31.5938,3.51562 30.1172,3.32812 28.8516,4.07812 L25.9219,5.85938 L25.4766,5.69531 L24.7031,2.32031 C24.3516,0.914062 23.1797,0 21.6797,0 L17.9766,0 C16.4766,0 15.3281,0.914062 14.9766,2.32031 L14.2031,5.69531 L13.7344,5.85938 L10.8047,4.07812 C9.5625,3.32812 8.08594,3.51562 7.03125,4.57031 L4.47656,7.125 C3.42188,8.22656 3.21094,9.67969 4.00781,10.9219 L5.8125,13.8516 L5.64844,14.2266 L2.32031,15.0469 C0.914062,15.3984 0,16.5938 0,18.0703 L0,21.6797 C0,23.1562 0.9375,24.3516 2.32031,24.7031 L5.69531,25.4766 L5.83594,25.8281 L4.03125,28.7578 C3.23438,29.9766 3.44531,31.4766 4.5,32.5312 L7.05469,35.0859 C8.10938,36.1641 9.60938,36.3516 10.8516,35.5781 L13.7812,33.7734 L14.2031,33.9141 L14.9766,37.2891 C15.3281,38.6953 16.4766,39.6094 17.9766,39.6094 Z M18.3047,36.3516 C18.0703,36.3516 17.9531,36.25781 17.9297,36.0469 L16.7812,31.4531 C15.5859,31.1484 14.5078,30.7031 13.6875,30.1406 L9.63281,32.6484 C9.46875,32.7422 9.30469,32.7188 9.14062,32.5781 L7.03125,30.4453 C6.86719,30.3047 6.86719,30.1406 6.98438,29.9531 L9.44531,25.9219 C9,25.125 8.48438,24.0469 8.17969,22.8516 L3.5625,21.7266 C3.35156,21.7031 3.25781,21.5859 3.25781,21.3516 L3.25781,18.3516 C3.25781,18.0938 3.32812,18.0234 3.5625,17.9531 L8.15625,16.8516 C8.46094,15.5625 9.07031,14.4375 9.39844,13.7344 L6.96094,9.72656 C6.82031,9.51562 6.82031,9.35156 6.98438,9.1875 L9.11719,7.10156 C9.28125,6.96094 9.39844,6.91406 9.63281,7.03125 L13.6406,9.46875 C14.4609,8.97656 15.6328,8.48438 16.8047,8.15625 L17.9297,3.5625 C17.9531,3.35156 18.0703,3.25781 18.3047,3.25781 L21.375,3.25781 C21.6094,3.25781 21.7266,3.35156 21.75,3.5625 L22.875,8.20312 C24.0938,8.50781 25.1484,9 25.9922,9.49219 L30.0469,7.03125 C30.25781,6.91406 30.375,6.96094 30.5625,7.10156 L32.6953,9.1875 C32.8359,9.35156 32.8594,9.51562 32.7188,9.72656 L30.25781,13.7344 C30.6094,14.4375 31.1953,15.5625 31.5,16.8516 L36.1172,17.9531 C36.3516,18.0234 36.3984,18.0938 36.3984,18.3516 L36.3984,21.3516 C36.3984,21.5859 36.3281,21.7031 36.1172,21.7266 L31.4766,22.8516 C31.1953,24.0469 30.6797,25.125 30.2109,25.9219 L32.6953,29.9531 C32.8125,30.1406 32.8125,30.3047 32.6484,30.4453 L30.5391,32.5781 C30.3516,32.7188 30.1875,32.7422 30.0469,32.6484 L25.9688,30.1406 C25.1719,30.7031 24.0703,31.1484 22.875,31.4531 L21.75,36.0469 C21.7266,36.25781 21.6094,36.3516 21.375,36.3516 L18.3047,36.3516 Z M19.8281,26.8125 C23.6719,26.8125 26.8359,23.6719 26.8359,19.8047 C26.8359,15.9844 23.6719,12.8438 19.8281,12.8438 C15.9844,12.8438 12.8203,15.9844 12.8203,19.8047 C12.8203,23.6484 15.9844,26.8125 19.8281,26.8125 Z M19.8281,23.5781 C17.7656,23.5781 16.1016,21.8906 16.1016,19.8047 C16.1016,17.7656 17.7656,16.0781 19.8281,16.0781 C21.8438,16.0781 23.5312,17.7656 23.5312,19.8047 C23.5312,21.8672 21.8438,23.5781 19.8281,23.5781 Z" id="形状" fill-opacity="0.85" fill="currentColor"></path>
8
+ </g>
9
+ </g>
10
+ </svg> -->
11
+ <svg width="24px" height="24px" viewBox="0 0 41.5547 40.9700126" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
12
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" fill-opacity="0.85">
13
+ <g fill="currentColor" fill-rule="nonzero">
14
+ <path d="M20.7891,38.3561 C21.2344,38.3561 21.6328,38.3092 22.125,38.2858 L23.2031,40.3483 C23.4375,40.7936 23.8828,41.028 24.375,40.9577 C24.8438,40.8639 25.2188,40.4655 25.2891,39.9733 L25.6172,37.6764 C26.4609,37.442 27.3047,37.1139 28.1484,36.7858 L29.8359,38.3092 C30.2109,38.6608 30.7031,38.7311 31.1719,38.4733 C31.5938,38.2155 31.7812,37.7467 31.6875,37.2545 L31.1953,34.9811 C31.8984,34.4655 32.625,33.903 33.2578,33.2702 L35.3906,34.1608 C35.8594,34.3717 36.3516,34.2311 36.7031,33.8092 C37.0078,33.4577 37.0312,32.942 36.7734,32.5202 L35.5547,30.5514 C36.0703,29.8249 36.4688,29.0514 36.8672,28.2077 L39.1875,28.3249 C39.6797,28.3483 40.125,28.0436 40.2891,27.5983 C40.4531,27.1295 40.2891,26.6139 39.9141,26.3327 L38.0859,24.8795 C38.3203,24.0358 38.5078,23.1686 38.5781,22.2311 L40.7578,21.528 C41.25,21.3639 41.5547,20.9889 41.5547,20.4733 C41.5547,19.9577 41.25,19.5827 40.7578,19.4186 L38.5781,18.7155 C38.5078,17.778 38.3203,16.9342 38.0859,16.067 L39.9141,14.6139 C40.2891,14.3327 40.4531,13.8405 40.2891,13.3717 C40.125,12.9264 39.6797,12.6217 39.1875,12.6452 L36.8672,12.7389 C36.4688,11.8952 36.0703,11.1452 35.5547,10.3952 L36.7734,8.42641998 C37.0312,8.02797998 37.0078,7.51235998 36.7031,7.16078998 C36.3516,6.73891998 35.8594,6.62172998 35.3906,6.80922998 L33.2578,7.67641998 C32.625,7.06703998 31.8984,6.48110998 31.1953,5.96547998 L31.6875,3.71547998 C31.7812,3.19985998 31.5938,2.73110998 31.1719,2.49672998 C30.7031,2.23891998 30.2109,2.28578998 29.8359,2.66078998 L28.1484,4.16078998 C27.3047,3.80922998 26.4609,3.52797998 25.6172,3.27016998 L25.2891,0.996730977 C25.2188,0.504542977 24.8438,0.106105977 24.375,0.0123557768 C23.8828,-0.0579567232 23.4141,0.176417977 23.2031,0.598292977 L22.125,2.66078998 C21.6328,2.63735998 21.2344,2.61391998 20.7891,2.61391998 C20.2969,2.61391998 19.8984,2.63735998 19.4297,2.66078998 L18.3281,0.598292977 C18.1172,0.176417977 17.6719,-0.0579567232 17.1562,0.0123557768 C16.6875,0.106105977 16.3359,0.504542977 16.2656,0.996730977 L15.9375,3.27016998 C15.0703,3.52797998 14.2266,3.80922998 13.4062,4.16078998 L11.6953,2.66078998 C11.3203,2.28578998 10.8281,2.23891998 10.3594,2.49672998 C9.96094,2.73110998 9.75,3.19985998 9.86719,3.71547998 L10.3359,5.96547998 C9.63281,6.48110998 8.90625,7.06703998 8.27344,7.67641998 L6.16406,6.80922998 C5.67188,6.62172998 5.20312,6.73891998 4.85156,7.16078998 C4.54688,7.51235998 4.5,8.02797998 4.75781,8.42641998 L5.97656,10.3952 C5.48438,11.1452 5.0625,11.8952 4.66406,12.7389 L2.34375,12.6452 C1.85156,12.6217 1.40625,12.9264 1.24219,13.3717 C1.10156,13.8405 1.24219,14.3092 1.64062,14.6139 L3.44531,16.067 C3.23438,16.9342 3.04688,17.778 2.97656,18.7155 L0.773438,19.4186 C0.304688,19.5827 0,19.9577 0,20.4733 C0,20.9889 0.304688,21.3639 0.773438,21.528 L2.97656,22.2311 C3.04688,23.1686 3.23438,24.0358 3.44531,24.8795 L1.64062,26.3327 C1.24219,26.6139 1.10156,27.1295 1.24219,27.5983 C1.40625,28.0436 1.85156,28.3483 2.34375,28.3249 L4.66406,28.2077 C5.0625,29.0514 5.48438,29.8249 5.97656,30.5514 L4.75781,32.5202 C4.5,32.942 4.54688,33.4577 4.85156,33.8092 C5.20312,34.2311 5.67188,34.3717 6.16406,34.1608 L8.27344,33.2702 C8.90625,33.903 9.63281,34.4655 10.3359,34.9811 L9.86719,37.2545 C9.77344,37.7467 9.96094,38.2155 10.3594,38.4733 C10.8281,38.7311 11.3203,38.6608 11.6953,38.3092 L13.4062,36.7858 C14.2266,37.1139 15.0703,37.442 15.9375,37.6764 L16.2656,39.9733 C16.3359,40.4655 16.6875,40.8639 17.1562,40.9577 C17.6719,41.028 18.0938,40.7936 18.3281,40.3483 L19.4297,38.2858 C19.8984,38.3092 20.2969,38.3561 20.7891,38.3561 Z M20.7891,35.1686 C12.5859,35.1686 6.25781,28.6295 6.25781,20.4967 C6.25781,12.3405 12.5859,5.80141998 20.7891,5.80141998 C28.9688,5.80141998 35.2969,12.3405 35.2969,20.4967 C35.2969,28.6295 28.9688,35.1686 20.7891,35.1686 Z M17.3672,17.4733 L19.6641,15.9967 L13.5938,5.66078998 L11.2266,7.02016998 L17.3672,17.4733 Z M25.1484,21.8327 L37.2188,21.8327 L37.1953,19.1608 L25.1484,19.1608 L25.1484,21.8327 Z M19.6406,25.0202 L17.3438,23.5202 L10.9922,33.8795 L13.3359,35.2858 L19.6406,25.0202 Z M20.7188,25.5124 C23.4844,25.5124 25.6875,23.2858 25.6875,20.5202 C25.6875,17.7545 23.4844,15.528 20.7188,15.528 C17.9531,15.528 15.7266,17.7545 15.7266,20.5202 C15.7266,23.2858 17.9531,25.5124 20.7188,25.5124 Z M20.7188,22.5592 C19.5703,22.5592 18.6562,21.6686 18.6562,20.5202 C18.6562,19.3717 19.5703,18.4811 20.7188,18.4811 C21.8672,18.4811 22.7578,19.3717 22.7578,20.5202 C22.7578,21.6686 21.8672,22.5592 20.7188,22.5592 Z"></path>
15
+ </g>
16
+ </g>
17
+ </svg>
18
+ </button>
19
+ <button id="chuanhu-manual-check-btn" onclick="manualCheckUpdate()" class="chuanhu-ui-btn">
20
+ <span class="show-on-latest">
21
+ <svg width="24px" height="24px" viewBox="0 0 45.2923004 37.8516" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
22
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
23
+ <g fill-rule="nonzero">
24
+ <path d="M0.415684357,19.0078 C-0.381190643,20.1094 -0.0296279434,21.1172 1.44693036,21.1172 L3.86100036,21.1172 C4.96256036,30.4453 13.0719004,37.8281 22.6344004,37.8281 C28.2360004,37.8281 33.2985004,35.3203 36.7672004,31.3828 C37.5641004,30.4922 37.4469004,29.3438 36.6266004,28.7578 C35.8063004,28.1719 34.8219004,28.3828 34.1188004,29.1562 C31.3297004,32.2969 27.2282004,34.2656 22.6344004,34.2656 C14.8532004,34.2656 8.52506036,28.5703 7.47037036,21.1172 L9.97818036,21.1172 C11.4313004,21.1172 11.8063004,20.1094 11.0329004,19.0312 L6.86100036,13.0781 C6.22818036,12.1641 5.24381036,12.1406 4.58756036,13.0781 L0.415684357,19.0078 Z M8.52506036,6.42188 C7.72818036,7.3125 7.84537036,8.4375 8.66568036,9.02344 C9.50943036,9.60938 10.4938004,9.44531 11.1969004,8.64844 C14.0094004,5.53125 18.0876004,3.5625 22.6344004,3.5625 C30.4157004,3.5625 36.7672004,9.25781 37.7985004,16.7109 L35.2907004,16.7109 C33.8376004,16.7109 33.4860004,17.7188 34.2594004,18.8203 L38.4313004,24.75 C39.0641004,25.6641 40.0485004,25.6875 40.7047004,24.75 L44.8766004,18.8438 C45.6735004,17.7188 45.3219004,16.7109 43.8454004,16.7109 L41.4313004,16.7109 C40.3297004,7.38281 32.2204004,0 22.6344004,0 C17.0797004,0 12.0172004,2.48438 8.52506036,6.42188 Z" fill-opacity="0.85" fill="currentColor"></path>
25
+ </g>
26
+ </g>
27
+ </svg>
28
+ </span>
29
+ <span class="show-on-outdated">
30
+ <svg width="24px" height="24px" viewBox="0 0 45.2924004 37.8516" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
31
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
32
+ <g fill="currentColor" fill-rule="nonzero">
33
+ <path d="M0.415682396,19.0078 C-0.381189604,20.1094 -0.0296276044,21.1172 1.4469304,21.1172 L3.8610104,21.1172 C4.9625604,30.4453 13.0719004,37.8281 22.6344004,37.8281 C28.2360004,37.8281 33.2985004,35.3203 36.7673004,31.3828 C37.5641004,30.4922 37.4469004,29.3437 36.6266004,28.7578 C35.8063004,28.1719 34.8219004,28.3828 34.1188004,29.1563 C31.3297004,32.2969 27.2282004,34.2656 22.6344004,34.2656 C14.8532004,34.2656 8.5250704,28.5703 7.4703704,21.1172 L9.9781804,21.1172 C11.4313004,21.1172 11.8063004,20.1094 11.0329004,19.0312 L6.8610104,13.0781 C6.2281804,12.1641 5.2437904,12.1406 4.5875404,13.0781 L0.415682396,19.0078 Z M8.5250704,6.42187 C7.7281804,7.31251 7.8453904,8.43749 8.6656604,9.02342 C9.5094604,9.60936 10.4938004,9.4453 11.1969004,8.64845 C14.0095004,5.53123 18.0875004,3.56251 22.6344004,3.56251 C30.4157004,3.56251 36.7673004,9.25781 37.7985004,16.7109 L35.2907004,16.7109 C33.8376004,16.7109 33.4860004,17.7188 34.2595004,18.8203 L38.4313004,24.75 C39.0641004,25.6641 40.0485004,25.6875 40.7048004,24.75 L44.8767004,18.8437 C45.6735004,17.7188 45.3222004,16.7109 43.8454004,16.7109 L41.4313004,16.7109 C40.3297004,7.38283 32.2204004,0 22.6344004,0 C17.0797004,0 12.0173004,2.48438 8.5250704,6.42187 Z" fill-opacity="0.85"></path>
34
+ <path d="M22.6344004,22.2422 C23.6422004,22.2422 24.2048004,21.6797 24.2282004,20.625 L24.5095004,11.0156 C24.5329004,9.96096 23.7126004,9.18749 22.6110004,9.18749 C21.5095004,9.18749 20.7126004,9.93749 20.7360004,10.9922 L21.0173004,20.625 C21.0407004,21.6797 21.6032004,22.2422 22.6344004,22.2422 Z M22.6344004,28.3828 C23.7829004,28.3828 24.7907004,27.4688 24.7907004,26.3203 C24.7907004,25.1484 23.8063004,24.2578 22.6344004,24.2578 C21.4626004,24.2578 20.5016004,25.1719 20.5016004,26.3203 C20.5016004,27.4453 21.4860004,28.3828 22.6344004,28.3828 Z" fill-opacity="0.85"></path>
35
+ </g>
36
+ </g>
37
+ </svg>
38
+ </span>
39
+ </button>
40
+ <!--
41
+ <button id="chuanhu-training-btn" onclick="openTrainingBox()" class="chuanhu-ui-btn">
42
+ <svg width="24px" height="24px" viewBox="0 0 37.3359 44.6953" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
43
+ <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
44
+ <g id="brain.filled.head.profile" fill-rule="nonzero">
45
+ <rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="37.3359" height="44.6953"></rect>
46
+ <path d="M21.5156,44.5312 C28.5,44.5312 32.7656,42.0469 32.7656,37.7109 L32.7656,28.7812 C35.6484,26.2031 37.3359,22.1016 37.3359,17.625 C37.3359,7.05469 30.4453,0 20.2031,0 C9.98438,0 3.14062,6.91406 3.14062,17.2266 C3.14062,17.7891 3.16406,18.2578 3.21094,18.7031 L0.960938,22.7109 C0.328125,23.8594 0,25.0078 0,26.0391 C0,27.9844 1.17188,29.5781 3.14062,30.1641 L3.14062,33.6562 C3.14062,37.1719 5.67188,38.8828 8.8125,38.5312 L12.8438,38.1328 L10.4531,35.5781 L10.4531,37.7109 C10.4531,42.0234 14.625,44.5312 21.5156,44.5312 Z M21.5156,41.1562 C16.5703,41.1562 13.6406,39.6328 13.6406,37.125 L13.6406,34.9453 C13.6406,34.7109 13.5234,34.5938 13.3125,34.6172 L8.34375,35.1797 C7.17188,35.2969 6.51562,34.8281 6.51562,33.4453 L6.51562,27.6562 C6.51562,27.4922 6.39844,27.375 6.16406,27.375 L5.13281,27.375 C4.03125,27.375 3.39844,26.8359 3.39844,25.9922 C3.39844,25.5234 3.5625,24.9844 3.89062,24.3984 L6.70312,19.3828 C6.58594,18.6562 6.51562,17.9062 6.51562,17.1797 C6.51562,8.90625 11.9766,3.39844 20.2031,3.39844 C28.4297,3.39844 33.9609,9.07031 33.9609,17.625 C33.9609,21.7266 32.2266,25.3359 29.3672,27.1172 L29.3672,37.125 C29.3672,39.6328 26.4375,41.1562 21.5156,41.1562 Z" id="形状" fill-opacity="0.85" fill="currentColor"></path>
47
+ <path d="M14.1797,19.7812 C16.2656,19.7812 17.7188,18.375 17.7188,16.4062 C17.7188,15.7031 17.5312,15.1875 17.2031,14.8359 C16.8516,14.5078 16.7109,14.1562 16.7109,13.9219 C16.7109,13.4062 17.1562,13.0312 17.6719,13.0312 C17.8828,13.0312 18.1641,13.0781 18.375,13.2891 C18.4219,13.3359 18.4688,13.4062 18.4922,13.4531 C19.9219,13.2891 20.6484,12.4688 20.6484,11.1328 C20.6484,10.6172 21.0703,10.1719 21.5859,10.1719 C22.1016,10.1719 22.5469,10.6406 22.5469,11.1328 C22.5703,13.2656 21.4922,14.6016 19.4297,15.0703 C19.5703,15.4922 19.6172,15.9609 19.6172,16.4297 C19.6172,18.2109 18.75,19.6641 17.3203,20.4844 C18.1172,20.8594 19.0312,21.0469 20.0156,21.0469 C20.4844,21.0469 20.9766,21 21.4688,20.9062 C21.375,20.6719 21.3516,20.4141 21.3516,20.2031 C21.3516,15.4453 28.1719,14.6484 28.1719,10.4531 C28.1719,8.34375 26.5781,6.72656 24.5859,6.72656 C23.8125,6.72656 23.6719,6.75 23.5078,6.77344 C22.7344,5.95312 21.6328,5.48438 20.6953,5.48438 C18.9844,5.48438 17.8125,6.5625 17.8125,8.15625 C17.8125,8.71875 17.4141,9.09375 16.8516,9.09375 C16.2891,9.09375 15.8906,8.67188 15.9141,8.10938 C15.9141,7.35938 16.1484,6.89062 16.2891,6.44531 C16.0078,6.39844 15.7266,6.375 15.4688,6.375 C13.2656,6.375 11.5547,7.73438 11.5547,9.39844 C11.5547,10.4766 12.2812,11.3438 13.2422,11.3438 C13.7812,11.3438 14.2031,11.7656 14.2031,12.2578 C14.2031,12.7969 13.7578,13.2422 13.2422,13.2422 C11.7656,13.2422 10.6172,12.5625 10.0547,11.4844 C9.51562,12.3516 9.21094,13.3594 9.21094,14.3906 C9.21094,17.4141 11.1562,19.7812 14.1797,19.7812 Z M27.7969,24.5156 C29.7656,24.5156 31.1953,22.3125 31.1953,19.3359 C31.1953,19.125 31.1953,18.8906 31.1953,18.6328 C30.4688,18.9609 29.6016,19.1016 28.6172,19.0312 C28.1016,18.9609 27.7031,18.5625 27.7031,18.0469 C27.7031,17.5312 28.1484,17.0625 28.6641,17.1094 C30.5859,17.2734 31.8516,16.2188 31.8516,14.5078 C31.8516,12.9375 31.125,11.625 29.8828,10.8047 C29.3203,15.9141 23.0156,16.5938 23.0156,20.1328 C23.0156,21.0938 23.6484,21.7734 24.75,21.7734 L25.0078,21.7734 C25.3125,23.3906 26.4375,24.5156 27.7969,24.5156 Z" id="形状" fill-opacity="0.85" fill="currentColor"></path>
48
+ </g>
49
+ </g>
50
+ </svg>
51
+ </button>
52
+ -->
53
+ </div>
54
+ <div class="btn-bar-group">
55
+ <button id="chuanhu-appearance-switcher" onclick="btnToggleDarkMode()" class="chuanhu-ui-btn">
56
+ <span class="show-on-dark">
57
+ <svg width="24px" height="24px" viewBox="0 0 39.9141 40.0547" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
58
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
59
+ <g fill-rule="nonzero">
60
+ <path d="M19.9688,7.42969 C20.9531,7.42969 21.7734,6.58594 21.7734,5.60156 L21.7734,1.80469 C21.7734,0.820312 20.9531,0 19.9688,0 C18.9609,0 18.1406,0.820312 18.1406,1.80469 L18.1406,5.60156 C18.1406,6.58594 18.9609,7.42969 19.9688,7.42969 Z M28.8281,11.1328 C29.5312,11.8359 30.7031,11.8594 31.4297,11.1328 L34.125,8.46094 C34.8047,7.75781 34.8047,6.5625 34.125,5.85938 C33.4219,5.15625 32.25,5.15625 31.5469,5.85938 L28.8281,8.57812 C28.1484,9.28125 28.1484,10.4297 28.8281,11.1328 Z M32.5078,20.0156 C32.5078,21 33.3516,21.8438 34.3359,21.8438 L38.0859,21.8438 C39.0938,21.8438 39.9141,21 39.9141,20.0156 C39.9141,19.0312 39.0938,18.1875 38.0859,18.1875 L34.3359,18.1875 C33.3516,18.1875 32.5078,19.0312 32.5078,20.0156 Z M28.8281,28.9219 C28.1484,29.625 28.1484,30.7734 28.8281,31.4766 L31.5469,34.1953 C32.25,34.8984 33.4219,34.875 34.125,34.1719 C34.8047,33.4688 34.8047,32.2969 34.125,31.5938 L31.4062,28.9219 C30.7031,28.2188 29.5312,28.2188 28.8281,28.9219 Z M19.9688,32.6016 C18.9609,32.6016 18.1406,33.4453 18.1406,34.4297 L18.1406,38.2266 C18.1406,39.2109 18.9609,40.0312 19.9688,40.0312 C20.9531,40.0312 21.7734,39.2109 21.7734,38.2266 L21.7734,34.4297 C21.7734,33.4453 20.9531,32.6016 19.9688,32.6016 Z M11.0859,28.9219 C10.3828,28.2188 9.1875,28.2188 8.48438,28.9219 L5.8125,31.5703 C5.10938,32.2734 5.10938,33.4453 5.78906,34.1484 C6.49219,34.8516 7.66406,34.875 8.36719,34.1719 L11.0625,31.4766 C11.7656,30.7734 11.7656,29.625 11.0859,28.9219 Z M7.40625,20.0156 C7.40625,19.0312 6.58594,18.1875 5.57812,18.1875 L1.82812,18.1875 C0.820312,18.1875 0,19.0312 0,20.0156 C0,21 0.820312,21.8438 1.82812,21.8438 L5.57812,21.8438 C6.58594,21.8438 7.40625,21 7.40625,20.0156 Z M11.0625,11.1328 C11.7656,10.4531 11.7656,9.25781 11.0859,8.57812 L8.39062,5.85938 C7.71094,5.17969 6.51562,5.15625 5.83594,5.85938 C5.13281,6.5625 5.13281,7.75781 5.8125,8.4375 L8.48438,11.1328 C9.1875,11.8359 10.3594,11.8359 11.0625,11.1328 Z" id="形状" fill-opacity="0.85" fill="currentColor"></path>
61
+ <path d="M19.9688,29.5781 C25.1719,29.5781 29.5078,25.2656 29.5078,20.0156 C29.5078,14.7656 25.1719,10.4531 19.9688,10.4531 C14.7422,10.4531 10.4062,14.7656 10.4062,20.0156 C10.4062,25.2656 14.7422,29.5781 19.9688,29.5781 Z M19.9688,26.3906 C16.4766,26.3906 13.5938,23.4844 13.5938,20.0156 C13.5938,16.5469 16.4766,13.6406 19.9688,13.6406 C23.4375,13.6406 26.3203,16.5469 26.3203,20.0156 C26.3203,23.4844 23.4375,26.3906 19.9688,26.3906 Z" id="形状" fill-opacity="0.85" fill="currentColor"></path>
62
+ </g>
63
+ </g>
64
+ </svg>
65
+ </span>
66
+ <span class="show-on-light">
67
+ <svg width="24px" height="24px" viewBox="0 0 37.2422 42.2109" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
68
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
69
+ <g fill="currentColor" fill-rule="nonzero">
70
+ <path d="M30,21.6797 C30.375,21.6797 30.6562,21.3984 30.7031,21 C31.2422,16.1016 31.5469,15.9141 36.5156,15.1406 C36.9844,15.0703 37.2422,14.8594 37.2422,14.4375 C37.2422,14.0625 36.9844,13.8047 36.6094,13.7578 C31.5938,12.7969 31.2422,12.7969 30.7031,7.89844 C30.6562,7.47656 30.375,7.21875 30,7.21875 C29.6016,7.21875 29.3438,7.47656 29.2969,7.875 C28.6875,12.8672 28.4531,13.1016 23.3672,13.7578 C23.0156,13.7812 22.7344,14.0625 22.7344,14.4375 C22.7344,14.8359 23.0156,15.0703 23.3672,15.1406 C28.4531,16.1016 28.6641,16.1484 29.2969,21.0469 C29.3438,21.3984 29.6016,21.6797 30,21.6797 Z M21.2109,9 C21.4453,9 21.5859,8.85938 21.6328,8.625 C22.2422,5.60156 22.1719,5.55469 25.3359,4.92188 C25.5469,4.875 25.7109,4.75781 25.7109,4.5 C25.7109,4.24219 25.5469,4.125 25.3359,4.07812 C22.1719,3.44531 22.2422,3.39844 21.6328,0.375 C21.5859,0.140625 21.4453,0 21.2109,0 C20.9531,0 20.8125,0.140625 20.7656,0.375 C20.1562,3.39844 20.2266,3.44531 17.0859,4.07812 C16.8516,4.125 16.6875,4.24219 16.6875,4.5 C16.6875,4.75781 16.8516,4.875 17.0859,4.92188 C20.2266,5.55469 20.1797,5.60156 20.7656,8.625 C20.8125,8.85938 20.9531,9 21.2109,9 Z" fill-opacity="0.85"></path>
71
+ <path d="M16.3125,38.625 C23.1094,38.625 28.5938,35.1562 31.0781,29.2734 C31.4297,28.4062 31.3125,27.75 30.9141,27.3516 C30.5625,27 29.9531,26.9297 29.2734,27.2109 C27.8906,27.75 26.2031,28.0547 24.1406,28.0547 C16.0078,28.0547 10.7578,22.9453 10.7578,15.0938 C10.7578,12.7734 11.2031,10.5 11.7188,9.49219 C12.0938,8.69531 12.0703,8.01562 11.7422,7.61719 C11.3672,7.17188 10.7109,7.00781 9.77344,7.35938 C3.98438,9.58594 0,15.6562 0,22.5938 C0,31.6875 6.79688,38.625 16.3125,38.625 Z M16.3594,35.4141 C8.69531,35.4141 3.21094,29.8359 3.21094,22.3359 C3.21094,17.9766 5.08594,14.0391 8.29688,11.4375 C7.89844,12.6094 7.6875,14.3438 7.6875,16.0078 C7.6875,24.9844 14.0156,31.1484 23.2969,31.1484 C24.8203,31.1484 26.2266,30.9844 26.9062,30.7734 C24.6094,33.6562 20.6953,35.4141 16.3594,35.4141 Z" fill-opacity="0.85"></path>
72
+ </g>
73
+ </g>
74
+ </svg>
75
+ </span>
76
+ </button>
77
+ </div>
78
+ </div>
web_assets/html/header_title.html ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div style="display:inline-flex;">
2
+ <buttom id="chuanhu-menu-btn" onclick='menuClick()'
3
+ class="chuanhu-ui-btn hover-round-btn" style="visibility: visible; width:42px; height:42px; margin-right:5px;">
4
+ <svg viewBox="0 0 24 24" fill="currentColor">
5
+ <path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"></path>
6
+ </svg>
7
+ </div>
8
+ <!-- <buttom class="gb_Ec gb_Ic gb_p" aria-label="Go back" title="Go back" role="button" tabindex="0">
9
+ <svg focusable="false" viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"></path>
10
+ </svg>
11
+ </buttom>
12
+ <div class="gb_Ec gb_Jc gb_p" aria-label="Close" role="button" tabindex="0"><svg viewBox="0 0 24 24">
13
+ <path
14
+ d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z">
15
+ </path>
16
+ </svg></div> -->
17
+ <div style="margin-left: 6px;">
18
+ <div>{app_title}</div>
19
+ </div>
20
+ </div>
web_assets/html/web_config.html ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div aria-label="config-div" style="display:none;">
2
+ <!-- app config -->
3
+ <div id="app_config">
4
+ <span id="enableCheckUpdate_config">{enableCheckUpdate_config}</span>
5
+ <span id="hideHistoryWhenNotLoggedIn_config">{hideHistoryWhenNotLoggedIn_config}</span>
6
+ </div>
7
+ <!-- i18n config -->
8
+ <div id="config_i18n">
9
+ <span id="forView_i18n">{forView_i18n}</span>
10
+ <span id="deleteConfirm_i18n_pref">{deleteConfirm_i18n_pref}</span>
11
+ <span id="deleteConfirm_i18n_suff">{deleteConfirm_i18n_suff}</span>
12
+ <span id="usingLatest_i18n">{usingLatest_i18n}</span>
13
+ <span id="updatingMsg_i18n">{updatingMsg_i18n}</span>
14
+ <span id="updateSuccess_i18n">{updateSuccess_i18n}</span>
15
+ <span id="updateFailure_i18n">{updateFailure_i18n}</span>
16
+ <span id="regenerate_i18n">{regenerate_i18n}</span>
17
+ <span id="deleteRound_i18n">{deleteRound_i18n}</span>
18
+ <span id="renameChat_i18n">{renameChat_i18n}</span>
19
+ <span id="validFileName_i18n">{validFileName_i18n}</span>
20
+ </div>
21
+ </div>
web_assets/icon/any-icon-512.png ADDED
web_assets/icon/mask-icon-512.png ADDED
web_assets/javascript/ChuanhuChat.js CHANGED
@@ -11,8 +11,11 @@ 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 chatbotIndicator = null;
 
 
16
  var chatbotWrap = null;
17
  var apSwitch = null;
18
  var messageBotDivs = null;
@@ -25,34 +28,52 @@ var sliders = null;
25
  var updateChuanhuBtn = null;
26
  var statusDisplay = null;
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, chatbotIndicator, 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
- chatbotIndicator = gradioApp().querySelector('#chuanhu-chatbot>div.wrap');
56
  chatbotWrap = gradioApp().querySelector('#chuanhu-chatbot > .wrapper > .wrap');
57
  apSwitch = gradioApp().querySelector('.apSwitch input[type="checkbox"]');
58
  updateToast = gradioApp().querySelector("#toast-update");
@@ -62,36 +83,50 @@ function initialize() {
62
  updateChuanhuBtn = gradioApp().getElementById("update-chuanhu-btn");
63
  statusDisplay = gradioApp().querySelector('#status-display');
64
 
65
- if (loginUserForm) {
66
- localStorage.setItem("userLogged", true);
67
- userLogged = true;
68
- }
 
 
 
 
 
69
 
70
- for (let elem in needInit) {
71
- if (needInit[elem] == null) {
72
- initialized = false;
73
- return;
74
- }
75
- }
76
 
77
- if (initialized) {
78
- adjustDarkMode();
79
- selectHistory();
80
- setTimeout(showOrHideUserInfo(), 2000);
81
- setChatbotHeight();
82
- setChatbotScroll();
83
- setSlider();
84
- setAvatar();
85
- if (!historyLoaded) loadHistoryHtml();
86
- if (!usernameGotten) getUserInfo();
87
- chatbotObserver.observe(chatbotIndicator, { attributes: true });
88
-
89
- const lastCheckTime = localStorage.getItem('lastCheckTime') || 0;
90
- const longTimeNoCheck = currentTime - lastCheckTime > 3 * 24 * 60 * 60 * 1000;
91
- if (longTimeNoCheck && !updateInfoGotten && !isLatestVersion || isLatestVersion && !updateInfoGotten) {
92
- updateLatestVersion();
93
- }
94
- }
 
 
 
 
 
 
 
 
 
 
 
95
  }
96
 
97
  function gradioApp() {
@@ -171,17 +206,49 @@ function disableSendBtn() {
171
  });
172
  }
173
 
174
- function adjustDarkMode() {
175
- function toggleDarkMode(isEnabled) {
176
- if (isEnabled) {
177
- document.body.classList.add("dark");
178
- document.body.style.setProperty("background-color", "var(--neutral-950)", "important");
 
 
 
 
179
  } else {
180
- document.body.classList.remove("dark");
181
- document.body.style.backgroundColor = "";
182
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  }
184
-
 
185
  const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
186
  apSwitch.checked = darkModeQuery.matches;
187
  toggleDarkMode(darkModeQuery.matches);
@@ -193,8 +260,55 @@ function adjustDarkMode() {
193
  toggleDarkMode(e.target.checked);
194
  });
195
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
  function setChatbotHeight() {
 
198
  const screenWidth = window.innerWidth;
199
  const statusDisplay = document.querySelector('#status-display');
200
  const statusDisplayHeight = statusDisplay ? statusDisplay.offsetHeight : 0;
@@ -221,70 +335,94 @@ function setChatbotScroll() {
221
  chatbotWrap.scrollTo(0,scrollHeight)
222
  }
223
 
224
- var botAvatarUrl = "";
225
- var userAvatarUrl = "";
226
- function setAvatar() {
227
- var botAvatar = gradioApp().getElementById("config-bot-avatar-url").innerText;
228
- var userAvatar = gradioApp().getElementById("config-user-avatar-url").innerText;
229
-
230
- if (botAvatar == "none") {
231
- botAvatarUrl = "";
232
- } else if (isImgUrl(botAvatar)) {
233
- botAvatarUrl = botAvatar;
234
- } else {
235
- // botAvatarUrl = "https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/70903329/aca3a7ec-4f1d-4667-890c-a6f47bf08f63";
236
- botAvatarUrl = "/file=web_assets/chatbot.png"
237
- }
238
-
239
- if (userAvatar == "none") {
240
- userAvatarUrl = "";
241
- } else if (isImgUrl(userAvatar)) {
242
- userAvatarUrl = userAvatar;
243
- } else {
244
- userAvatarUrl = "data:image/svg+xml,%3Csvg width='32px' height='32px' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cg stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Crect fill-opacity='0.5' fill='%23bbbbbb' x='0' y='0' width='32' height='32'%3E%3C/rect%3E%3Cg transform='translate(5, 4)' fill='%23999999' fill-opacity='0.8' fill-rule='nonzero'%3E%3Cpath d='M2.29372246,24 L19.7187739,24 C20.4277609,24 20.985212,23.8373915 21.3911272,23.5121746 C21.7970424,23.1869576 22,22.7418004 22,22.1767029 C22,21.3161536 21.7458721,20.4130827 21.2376163,19.4674902 C20.7293605,18.5218977 19.9956681,17.6371184 19.036539,16.8131524 C18.07741,15.9891863 16.9210688,15.3177115 15.5675154,14.798728 C14.2139621,14.2797445 12.6914569,14.0202527 11,14.0202527 C9.30854307,14.0202527 7.78603793,14.2797445 6.43248458,14.798728 C5.07893122,15.3177115 3.92259002,15.9891863 2.96346097,16.8131524 C2.00433193,17.6371184 1.27063951,18.5218977 0.762383704,19.4674902 C0.254127901,20.4130827 0,21.3161536 0,22.1767029 C0,22.7418004 0.202957595,23.1869576 0.608872784,23.5121746 C1.01478797,23.8373915 1.57640453,24 2.29372246,24 Z M11.0124963,11.6521659 C11.9498645,11.6521659 12.8155943,11.3906214 13.6096856,10.8675324 C14.403777,10.3444433 15.042131,9.63605539 15.5247478,8.74236856 C16.0073646,7.84868174 16.248673,6.84722464 16.248673,5.73799727 C16.248673,4.65135034 16.0071492,3.67452644 15.5241015,2.80752559 C15.0410538,1.94052474 14.4024842,1.25585359 13.6083929,0.753512156 C12.8143016,0.251170719 11.9490027,0 11.0124963,0 C10.0759899,0 9.20860836,0.255422879 8.41035158,0.766268638 C7.6120948,1.2771144 6.97352528,1.96622098 6.49464303,2.8335884 C6.01576078,3.70095582 5.77631966,4.67803631 5.77631966,5.76482987 C5.77631966,6.86452653 6.01554533,7.85912886 6.49399667,8.74863683 C6.97244801,9.63814481 7.60871935,10.3444433 8.40281069,10.8675324 C9.19690203,11.3906214 10.0667972,11.6521659 11.0124963,11.6521659 Z'%3E%3C/path%3E%3C/g%3E%3C/g%3E%3C/svg%3E";
245
- }
246
- }
247
-
248
- function clearChatbot() {
249
  clearHistoryHtml();
250
- clearMessageRows();
 
251
  }
252
 
253
- function chatbotContentChanged(attempt = 1) {
254
  for (var i = 0; i < attempt; i++) {
255
  setTimeout(() => {
256
  // clearMessageRows();
257
  saveHistoryHtml();
258
  disableSendBtn();
259
- gradioApp().querySelectorAll('#chuanhu-chatbot .message-wrap .message.user').forEach((userElement) => {addAvatars(userElement, 'user')});
260
- gradioApp().querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach((botElement) => {addAvatars(botElement, 'bot'); addChuanhuButton(botElement)});
261
- }, i === 0 ? 0 : 500);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  }
263
  // 理论上是不需要多次尝试执行的,可惜gradio的bug导致message可能没有渲染完毕,所以尝试500ms后再次执行
264
  }
265
 
266
  var chatbotObserver = new MutationObserver(() => {
267
- clearMessageRows();
268
  chatbotContentChanged(1);
269
  if (chatbotIndicator.classList.contains('hide')) {
 
 
 
 
270
  chatbotContentChanged(2);
271
  }
 
 
 
 
 
272
  });
273
 
274
  // 监视页面内部 DOM 变动
275
- var observer = new MutationObserver(function (mutations) {
276
- gradioLoaded(mutations);
 
 
 
 
 
 
 
277
  });
278
 
279
  // 监视页面变化
280
  window.addEventListener("DOMContentLoaded", function () {
281
- const ga = document.getElementsByTagName("gradio-app");
282
- observer.observe(ga[0], { childList: true, subtree: true });
 
 
283
  isInIframe = (window.self !== window.top);
284
  historyLoaded = false;
285
  });
286
- window.addEventListener('resize', setChatbotHeight);
287
- window.addEventListener('scroll', function(){setChatbotHeight(); setUpdateWindowHeight();});
 
 
 
 
 
 
 
 
 
 
 
 
288
  window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", adjustDarkMode);
289
 
290
  // console suprise
@@ -303,13 +441,13 @@ function makeML(str) {
303
  return l
304
  }
305
  let ChuanhuInfo = function () {
306
- /*
307
- ________ __ ________ __
308
  / ____/ /_ __ ______ _____ / /_ __ __ / ____/ /_ ____ _/ /_
309
  / / / __ \/ / / / __ `/ __ \/ __ \/ / / / / / / __ \/ __ `/ __/
310
- / /___/ / / / /_/ / /_/ / / / / / / / /_/ / / /___/ / / / /_/ / /_
311
- \____/_/ /_/\__,_/\__,_/_/ /_/_/ /_/\__,_/ \____/_/ /_/\__,_/\__/
312
-
313
  川虎Chat (Chuanhu Chat) - GUI for ChatGPT API and many LLMs
314
  */
315
  }
@@ -318,11 +456,5 @@ let description = `
318
  GitHub repository: [https://github.com/GaiZhenbiao/ChuanhuChatGPT]\n
319
  Enjoy our project!\n
320
  `
321
- console.log(`%c${makeML(ChuanhuInfo)}`,styleTitle1)
322
- console.log(`%c${description}`, styleDesc1)
323
-
324
- // button svg code
325
- 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>';
326
- 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>';
327
- 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>';
328
- 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>';
 
11
  var user_input_tb = null;
12
  var userInfoDiv = null;
13
  var appTitleDiv = null;
14
+ var chatbotArea = null;
15
  var chatbot = null;
16
  var chatbotIndicator = null;
17
+ var uploaderIndicator = null;
18
+ var chatListIndicator = null;
19
  var chatbotWrap = null;
20
  var apSwitch = null;
21
  var messageBotDivs = null;
 
28
  var updateChuanhuBtn = null;
29
  var statusDisplay = null;
30
 
31
+ var historySelector = null;
32
+ var chuanhuPopup = null;
33
+ var settingBox = null;
34
+ var trainingBox = null;
35
+ var popupWrapper = null;
36
+ var chuanhuHeader = null;
37
+ var menu = null;
38
+ var toolbox = null;
39
+ // var trainBody = null;
40
+
41
  var isInIframe = (window.self !== window.top);
42
  var currentTime = new Date().getTime();
 
43
 
44
+ let windowWidth = window.innerWidth; // 初始窗口宽度
45
+
46
+ function addInit() {
47
+ var needInit = {chatbotIndicator, uploaderIndicator};
48
+
49
+ chatbotIndicator = gradioApp().querySelector('#chuanhu-chatbot > div.wrap');
50
+ uploaderIndicator = gradioApp().querySelector('#upload-index-file > div.wrap');
51
+ chatListIndicator = gradioApp().querySelector('#history-select-dropdown > div.wrap');
52
+
53
+ for (let elem in needInit) {
54
+ if (needInit[elem] == null) {
55
+ // addInited = false;
56
+ return false;
57
  }
58
  }
59
+
60
+ chatbotObserver.observe(chatbotIndicator, { attributes: true, childList: true, subtree: true });
61
+ chatListObserver.observe(chatListIndicator, { attributes: true });
62
+ setUploader();
63
+
64
+ return true;
65
  }
66
 
67
  function initialize() {
68
+ gradioObserver.observe(gradioApp(), { childList: true, subtree: true });
 
69
 
70
  loginUserForm = gradioApp().querySelector(".gradio-container > .main > .wrap > .panel > .form")
71
  gradioContainer = gradioApp().querySelector(".gradio-container");
72
  user_input_tb = gradioApp().getElementById('user-input-tb');
73
  userInfoDiv = gradioApp().getElementById("user-info");
74
  appTitleDiv = gradioApp().getElementById("app-title");
75
+ chatbotArea = gradioApp().querySelector('#chatbot-area');
76
  chatbot = gradioApp().querySelector('#chuanhu-chatbot');
 
77
  chatbotWrap = gradioApp().querySelector('#chuanhu-chatbot > .wrapper > .wrap');
78
  apSwitch = gradioApp().querySelector('.apSwitch input[type="checkbox"]');
79
  updateToast = gradioApp().querySelector("#toast-update");
 
83
  updateChuanhuBtn = gradioApp().getElementById("update-chuanhu-btn");
84
  statusDisplay = gradioApp().querySelector('#status-display');
85
 
86
+ historySelector = gradioApp().querySelector('#history-select-dropdown');
87
+ chuanhuPopup = gradioApp().querySelector('#chuanhu-popup');
88
+ settingBox = gradioApp().querySelector('#chuanhu-setting');
89
+ trainingBox = gradioApp().querySelector('#chuanhu-training');
90
+ popupWrapper = gradioApp().querySelector('#popup-wrapper');
91
+ chuanhuHeader = gradioApp().querySelector('#chuanhu-header');
92
+ menu = gradioApp().querySelector('#menu-area');
93
+ toolbox = gradioApp().querySelector('#toolbox-area');
94
+ // trainBody = gradioApp().querySelector('#train-body');
95
 
96
+ // if (loginUserForm) {
97
+ // localStorage.setItem("userLogged", true);
98
+ // userLogged = true;
99
+ // }
 
 
100
 
101
+ adjustDarkMode();
102
+ adjustSide();
103
+ setChatList();
104
+ setChatListHeader();
105
+ setLoclize();
106
+ selectHistory();
107
+ // setChatbotHeight();
108
+ setPopupBoxPosition();
109
+ setSlider();
110
+ setCheckboxes();
111
+ checkModel();
112
+
113
+ settingBox.classList.add('hideBox');
114
+ trainingBox.classList.add('hideBox');
115
+
116
+ if (!historyLoaded) loadHistoryHtml();
117
+ if (!usernameGotten) getUserInfo();
118
+
119
+ setUpdater();
120
+
121
+ setChatbotScroll();
122
+ setTimeout(showOrHideUserInfo(), 2000);
123
+
124
+ // setHistroyPanel();
125
+ // trainBody.classList.add('hide-body');
126
+
127
+
128
+
129
+ return true;
130
  }
131
 
132
  function gradioApp() {
 
206
  });
207
  }
208
 
209
+ function checkModel() {
210
+ const model = gradioApp().querySelector('#model-select-dropdown input');
211
+ var modelValue = model.value;
212
+ checkGPT();
213
+ checkXMChat();
214
+ function checkGPT() {
215
+ modelValue = model.value;
216
+ if (modelValue.toLowerCase().includes('gpt')) {
217
+ gradioApp().querySelector('#header-btn-groups').classList.add('is-gpt');
218
  } else {
219
+ gradioApp().querySelector('#header-btn-groups').classList.remove('is-gpt');
 
220
  }
221
+ // console.log('gpt model checked')
222
+ }
223
+ function checkXMChat() {
224
+ modelValue = model.value;
225
+ if (modelValue.includes('xmchat')) {
226
+ chatbotArea.classList.add('is-xmchat');
227
+ } else {
228
+ chatbotArea.classList.remove('is-xmchat');
229
+ }
230
+ }
231
+
232
+ model.addEventListener('blur', ()=>{
233
+ setTimeout(()=>{
234
+ checkGPT();
235
+ checkXMChat();
236
+ }, 100);
237
+ });
238
+ }
239
+
240
+ function toggleDarkMode(isEnabled) {
241
+ if (isEnabled) {
242
+ document.body.classList.add("dark");
243
+ document.querySelector('meta[name="theme-color"]').setAttribute('content', '#171717');
244
+ document.body.style.setProperty("background-color", "var(--neutral-950)", "important");
245
+ } else {
246
+ document.body.classList.remove("dark");
247
+ document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff');
248
+ document.body.style.backgroundColor = "";
249
  }
250
+ }
251
+ function adjustDarkMode() {
252
  const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
253
  apSwitch.checked = darkModeQuery.matches;
254
  toggleDarkMode(darkModeQuery.matches);
 
260
  toggleDarkMode(e.target.checked);
261
  });
262
  }
263
+ function btnToggleDarkMode() {
264
+ apSwitch.checked = !apSwitch.checked;
265
+ toggleDarkMode(apSwitch.checked);
266
+ }
267
+
268
+ function setScrollShadow() {
269
+ const toolboxScroll = toolbox.querySelector('#toolbox-area > .gradio-box > .gradio-tabs > div.tab-nav');
270
+ const toolboxTabs = toolboxScroll.querySelectorAll('button');
271
+ let toolboxScrollWidth = 0;
272
+ toolboxTabs.forEach((tab) => {
273
+ toolboxScrollWidth += tab.offsetWidth; // 获取按钮宽度并累加
274
+ });
275
+ function adjustScrollShadow() {
276
+ if (toolboxScroll.scrollLeft > 0) {
277
+ toolboxScroll.classList.add('scroll-shadow-left');
278
+ } else {
279
+ toolboxScroll.classList.remove('scroll-shadow-left');
280
+ }
281
+
282
+ if (toolboxScroll.scrollLeft + toolboxScroll.clientWidth < toolboxScrollWidth) {
283
+ toolboxScroll.classList.add('scroll-shadow-right');
284
+ } else {
285
+ toolboxScroll.classList.remove('scroll-shadow-right');
286
+ }
287
+ }
288
+ toolboxScroll.addEventListener('scroll', () => {
289
+ adjustScrollShadow();
290
+ });
291
+ // no, I failed to make shadow appear on the top layer...
292
+ }
293
+
294
+ function setPopupBoxPosition() {
295
+ const screenWidth = window.innerWidth;
296
+ const screenHeight = window.innerHeight;
297
+ popupWrapper.style.height = `${screenHeight}px`;
298
+ popupWrapper.style.width = `${screenWidth}px`;
299
+ // const popupBoxWidth = 680;
300
+ // const popupBoxHeight = 400;
301
+ // chuanhuPopup.style.left = `${(screenWidth - popupBoxWidth) / 2}px`;
302
+ // chuanhuPopup.style.top = `${(screenHeight - popupBoxHeight) / 2}px`;
303
+ }
304
+
305
+ function updateVH() {
306
+ const vh = window.innerHeight * 0.01;
307
+ document.documentElement.style.setProperty('--vh', `${vh}px`);
308
+ }
309
 
310
  function setChatbotHeight() {
311
+ return;
312
  const screenWidth = window.innerWidth;
313
  const statusDisplay = document.querySelector('#status-display');
314
  const statusDisplayHeight = statusDisplay ? statusDisplay.offsetHeight : 0;
 
335
  chatbotWrap.scrollTo(0,scrollHeight)
336
  }
337
 
338
+ function clearChatbot(a, b) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
  clearHistoryHtml();
340
+ // clearMessageRows();
341
+ return [a, b]
342
  }
343
 
344
+ function chatbotContentChanged(attempt = 1, force = false) {
345
  for (var i = 0; i < attempt; i++) {
346
  setTimeout(() => {
347
  // clearMessageRows();
348
  saveHistoryHtml();
349
  disableSendBtn();
350
+
351
+ gradioApp().querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
352
+
353
+ if (chatbotIndicator.classList.contains('hide')) { // generation finished
354
+ setLatestMessage();
355
+ setChatList();
356
+ }
357
+
358
+ if (!chatbotIndicator.classList.contains('translucent')) { // message deleted
359
+ var checkLatestAdded = setInterval(() => {
360
+ var latestMessageNow = gradioApp().querySelector('#chuanhu-chatbot > .wrapper > .wrap > .message-wrap .message.bot.latest');
361
+ if (latestMessageNow && latestMessageNow.querySelector('.message-btn-row')) {
362
+ clearInterval(checkLatestAdded);
363
+ } else {
364
+ setLatestMessage();
365
+ }
366
+ }, 200);
367
+ }
368
+
369
+
370
+ }, i === 0 ? 0 : 200);
371
  }
372
  // 理论上是不需要多次尝试执行的,可惜gradio的bug导致message可能没有渲染完毕,所以尝试500ms后再次执行
373
  }
374
 
375
  var chatbotObserver = new MutationObserver(() => {
 
376
  chatbotContentChanged(1);
377
  if (chatbotIndicator.classList.contains('hide')) {
378
+ // setLatestMessage();
379
+ chatbotContentChanged(2);
380
+ }
381
+ if (!chatbotIndicator.classList.contains('translucent')) {
382
  chatbotContentChanged(2);
383
  }
384
+
385
+ });
386
+
387
+ var chatListObserver = new MutationObserver(() => {
388
+ setChatList();
389
  });
390
 
391
  // 监视页面内部 DOM 变动
392
+ var gradioObserver = new MutationObserver(function (mutations) {
393
+ for (var i = 0; i < mutations.length; i++) {
394
+ if (mutations[i].addedNodes.length) {
395
+ if (addInit()) {
396
+ gradioObserver.disconnect();
397
+ return;
398
+ }
399
+ }
400
+ }
401
  });
402
 
403
  // 监视页面变化
404
  window.addEventListener("DOMContentLoaded", function () {
405
+ // const ga = document.getElementsByTagName("gradio-app");
406
+ updateVH();
407
+ windowWidth = window.innerWidth;
408
+ gradioApp().addEventListener("render", initialize);
409
  isInIframe = (window.self !== window.top);
410
  historyLoaded = false;
411
  });
412
+ window.addEventListener('resize', ()=>{
413
+ // setChatbotHeight();
414
+ updateVH();
415
+ windowWidth = window.innerWidth;
416
+ setPopupBoxPosition();
417
+ adjustSide();
418
+ });
419
+ window.addEventListener('orientationchange', (event) => {
420
+ updateVH();
421
+ windowWidth = window.innerWidth;
422
+ setPopupBoxPosition();
423
+ adjustSide();
424
+ });
425
+ window.addEventListener('scroll', ()=>{setPopupBoxPosition();});
426
  window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", adjustDarkMode);
427
 
428
  // console suprise
 
441
  return l
442
  }
443
  let ChuanhuInfo = function () {
444
+ /*
445
+ ________ __ ________ __
446
  / ____/ /_ __ ______ _____ / /_ __ __ / ____/ /_ ____ _/ /_
447
  / / / __ \/ / / / __ `/ __ \/ __ \/ / / / / / / __ \/ __ `/ __/
448
+ / /___/ / / / /_/ / /_/ / / / / / / / /_/ / / /___/ / / / /_/ / /_
449
+ \____/_/ /_/\__,_/\__,_/_/ /_/_/ /_/\__,_/ \____/_/ /_/\__,_/\__/
450
+
451
  川虎Chat (Chuanhu Chat) - GUI for ChatGPT API and many LLMs
452
  */
453
  }
 
456
  GitHub repository: [https://github.com/GaiZhenbiao/ChuanhuChatGPT]\n
457
  Enjoy our project!\n
458
  `
459
+ console.log(`%c${makeML(ChuanhuInfo)}`,styleTitle1);
460
+ console.log(`%c${description}`, styleDesc1);