Keldos commited on
Commit
0c0edf5
·
2 Parent(s): b71c4e4 0e91c63

Merge branch 'main' into UI-new

Browse files
.gitignore CHANGED
@@ -145,3 +145,4 @@ lora/
145
  .idea
146
  templates/*
147
  files/
 
 
145
  .idea
146
  templates/*
147
  files/
148
+ tmp/
ChuanhuChatbot.py CHANGED
@@ -44,7 +44,7 @@ with gr.Blocks(theme=small_and_beautiful_theme) as demo:
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(),
@@ -98,7 +98,7 @@ with gr.Blocks(theme=small_and_beautiful_theme) as demo:
98
  with gr.Column(elem_id="chuanhu-area", scale=5):
99
  with gr.Column(scale=5, elem_id="chatbot-area"):
100
  with gr.Row():
101
- chatbot = gr.Chatbot(label="Chuanhu Chat", elem_id="chuanhu-chatbot", latex_delimiters=latex_delimiters_set, height=700)
102
  with gr.Row():
103
  with gr.Column(min_width=225, scale=12):
104
  user_input = gr.Textbox(
 
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(), 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(),
 
98
  with gr.Column(elem_id="chuanhu-area", scale=5):
99
  with gr.Column(scale=5, elem_id="chatbot-area"):
100
  with gr.Row():
101
+ chatbot = gr.Chatbot(label="Chuanhu Chat", elem_id="chuanhu-chatbot", latex_delimiters=latex_delimiters_set, height=700, avatar_images=[config.user_avatar, config.bot_avatar])
102
  with gr.Row():
103
  with gr.Column(min_width=225, scale=12):
104
  user_input = gr.Textbox(
config_example.json CHANGED
@@ -7,6 +7,11 @@
7
  "xmchat_api_key": "", // 你的 xmchat API Key,用于 XMChat 对话模型
8
  "minimax_api_key": "", // 你的 MiniMax API Key,用于 MiniMax 对话模型
9
  "minimax_group_id": "", // 你的 MiniMax Group ID,用于 MiniMax 对话模型
 
 
 
 
 
10
 
11
  //== Azure ==
12
  "openai_api_type": "openai", // 可选项:azure, openai
@@ -24,8 +29,8 @@
24
  "hide_history_when_not_logged_in": false, //未登录情况下是否不展示对话历史
25
  "check_update": true, //是否启用检查更新
26
  "default_model": "gpt-3.5-turbo", // 默认模型
27
- "bot_avatar": "default", // 机器人头像,可填写图片链接、Data URL (base64),或者"none"(不显示头像)
28
- "user_avatar": "default", // 用户头像,可填写图片链接、Data URL (base64),或者"none"(不显示头像)
29
 
30
  //== API 用量 ==
31
  "show_api_billing": false, //是否显示OpenAI API用量(启用需要填写sensitive_id)
 
7
  "xmchat_api_key": "", // 你的 xmchat API Key,用于 XMChat 对话模型
8
  "minimax_api_key": "", // 你的 MiniMax API Key,用于 MiniMax 对话模型
9
  "minimax_group_id": "", // 你的 MiniMax Group ID,用于 MiniMax 对话模型
10
+ "midjourney_proxy_api_base": "https://xxx/mj", // 你的 https://github.com/novicezk/midjourney-proxy 代理地址
11
+ "midjourney_proxy_api_secret": "", // 你的 MidJourney Proxy API Secret,用于鉴权访问 api,可选
12
+ "midjourney_discord_proxy_url": "", // 你的 MidJourney Discord Proxy URL,用于对生成对图进行反代,可选
13
+ "midjourney_temp_folder": "./tmp", // 你的 MidJourney 临时文件夹,用于存放生成的图片,填空则关闭自动下载切图(直接显示MJ的四宫格图)
14
+
15
 
16
  //== Azure ==
17
  "openai_api_type": "openai", // 可选项:azure, openai
 
29
  "hide_history_when_not_logged_in": false, //未登录情况下是否不展示对话历史
30
  "check_update": true, //是否启用检查更新
31
  "default_model": "gpt-3.5-turbo", // 默认模型
32
+ "bot_avatar": "default", // 机器人头像,可填写本地或网络图片链接,或者"none"(不显示头像)
33
+ "user_avatar": "default", // 用户头像,可填写本地或网络图片链接,或者"none"(不显示头像)
34
 
35
  //== API 用量 ==
36
  "show_api_billing": false, //是否显示OpenAI API用量(启用需要填写sensitive_id)
modules/config.py CHANGED
@@ -114,6 +114,15 @@ os.environ["MINIMAX_API_KEY"] = minimax_api_key
114
  minimax_group_id = config.get("minimax_group_id", "")
115
  os.environ["MINIMAX_GROUP_ID"] = minimax_group_id
116
 
 
 
 
 
 
 
 
 
 
117
  load_config_to_environ(["openai_api_type", "azure_openai_api_key", "azure_openai_api_base_url",
118
  "azure_openai_api_version", "azure_deployment_name", "azure_embedding_deployment_name", "azure_embedding_model_name"])
119
 
@@ -268,4 +277,12 @@ share = config.get("share", False)
268
 
269
  # avatar
270
  bot_avatar = config.get("bot_avatar", "default")
271
- user_avatar = config.get("user_avatar", "default")
 
 
 
 
 
 
 
 
 
114
  minimax_group_id = config.get("minimax_group_id", "")
115
  os.environ["MINIMAX_GROUP_ID"] = minimax_group_id
116
 
117
+ midjourney_proxy_api_base = config.get("midjourney_proxy_api_base", "")
118
+ os.environ["MIDJOURNEY_PROXY_API_BASE"] = midjourney_proxy_api_base
119
+ midjourney_proxy_api_secret = config.get("midjourney_proxy_api_secret", "")
120
+ os.environ["MIDJOURNEY_PROXY_API_SECRET"] = midjourney_proxy_api_secret
121
+ midjourney_discord_proxy_url = config.get("midjourney_discord_proxy_url", "")
122
+ os.environ["MIDJOURNEY_DISCORD_PROXY_URL"] = midjourney_discord_proxy_url
123
+ midjourney_temp_folder = config.get("midjourney_temp_folder", "")
124
+ os.environ["MIDJOURNEY_TEMP_FOLDER"] = midjourney_temp_folder
125
+
126
  load_config_to_environ(["openai_api_type", "azure_openai_api_key", "azure_openai_api_base_url",
127
  "azure_openai_api_version", "azure_deployment_name", "azure_embedding_deployment_name", "azure_embedding_model_name"])
128
 
 
277
 
278
  # avatar
279
  bot_avatar = config.get("bot_avatar", "default")
280
+ user_avatar = config.get("user_avatar", "default")
281
+ if bot_avatar == "" or bot_avatar == "none" or bot_avatar is None:
282
+ bot_avatar = None
283
+ elif bot_avatar == "default":
284
+ bot_avatar = "web_assets/chatbot.png"
285
+ if user_avatar == "" or user_avatar == "none" or user_avatar is None:
286
+ user_avatar = None
287
+ elif user_avatar == "default":
288
+ user_avatar = "web_assets/user.png"
modules/models/base_model.py CHANGED
@@ -141,6 +141,7 @@ class ModelType(Enum):
141
  ChuanhuAgent = 8
142
  GooglePaLM = 9
143
  LangchainChat = 10
 
144
 
145
  @classmethod
146
  def get_type(cls, model_name: str):
@@ -166,7 +167,9 @@ class ModelType(Enum):
166
  model_type = ModelType.ChuanhuAgent
167
  elif "palm" in model_name_lower:
168
  model_type = ModelType.GooglePaLM
169
- elif "azure" or "api" in model_name_lower:
 
 
170
  model_type = ModelType.LangchainChat
171
  else:
172
  model_type = ModelType.Unknown
 
141
  ChuanhuAgent = 8
142
  GooglePaLM = 9
143
  LangchainChat = 10
144
+ Midjourney = 11
145
 
146
  @classmethod
147
  def get_type(cls, model_name: str):
 
167
  model_type = ModelType.ChuanhuAgent
168
  elif "palm" in model_name_lower:
169
  model_type = ModelType.GooglePaLM
170
+ elif "midjourney" in model_name_lower:
171
+ model_type = ModelType.Midjourney
172
+ elif "azure" in model_name_lower or "api" in model_name_lower:
173
  model_type = ModelType.LangchainChat
174
  else:
175
  model_type = ModelType.Unknown
modules/models/midjourney.py ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import io
3
+ import json
4
+ import logging
5
+ import pathlib
6
+ import time
7
+ import tempfile
8
+ import os
9
+
10
+ from datetime import datetime
11
+
12
+ import requests
13
+ import tiktoken
14
+ from PIL import Image
15
+
16
+ from modules.config import retrieve_proxy
17
+ from modules.models.models import XMChat
18
+
19
+ mj_proxy_api_base = os.getenv("MIDJOURNEY_PROXY_API_BASE")
20
+ mj_discord_proxy_url = os.getenv("MIDJOURNEY_DISCORD_PROXY_URL")
21
+ mj_temp_folder = os.getenv("MIDJOURNEY_TEMP_FOLDER")
22
+
23
+
24
+ class Midjourney_Client(XMChat):
25
+
26
+ class FetchDataPack:
27
+ """
28
+ A class to store data for current fetching data from Midjourney API
29
+ """
30
+
31
+ action: str # current action, e.g. "IMAGINE", "UPSCALE", "VARIATION"
32
+ prefix_content: str # prefix content, task description and process hint
33
+ task_id: str # task id
34
+ start_time: float # task start timestamp
35
+ timeout: int # task timeout in seconds
36
+ finished: bool # whether the task is finished
37
+ prompt: str # prompt for the task
38
+
39
+ def __init__(self, action, prefix_content, task_id, timeout=900):
40
+ self.action = action
41
+ self.prefix_content = prefix_content
42
+ self.task_id = task_id
43
+ self.start_time = time.time()
44
+ self.timeout = timeout
45
+ self.finished = False
46
+
47
+ def __init__(self, model_name, api_key, user_name=""):
48
+ super().__init__(api_key, user_name)
49
+ self.model_name = model_name
50
+ self.history = []
51
+ self.api_key = api_key
52
+ self.headers = {
53
+ "Content-Type": "application/json",
54
+ "mj-api-secret": f"{api_key}"
55
+ }
56
+ self.proxy_url = mj_proxy_api_base
57
+ self.command_splitter = "::"
58
+
59
+ if mj_temp_folder:
60
+ temp = "./tmp"
61
+ if user_name:
62
+ temp = os.path.join(temp, user_name)
63
+ if not os.path.exists(temp):
64
+ os.makedirs(temp)
65
+ self.temp_path = tempfile.mkdtemp(dir=temp)
66
+ logging.info("mj temp folder: " + self.temp_path)
67
+ else:
68
+ self.temp_path = None
69
+
70
+ def use_mj_self_proxy_url(self, img_url):
71
+ """
72
+ replace discord cdn url with mj self proxy url
73
+ """
74
+ return img_url.replace(
75
+ "https://cdn.discordapp.com/",
76
+ mj_discord_proxy_url and mj_discord_proxy_url or "https://cdn.discordapp.com/"
77
+ )
78
+
79
+ def split_image(self, image_url):
80
+ """
81
+ when enabling temp dir, split image into 4 parts
82
+ """
83
+ with retrieve_proxy():
84
+ image_bytes = requests.get(image_url).content
85
+ img = Image.open(io.BytesIO(image_bytes))
86
+ width, height = img.size
87
+ # calculate half width and height
88
+ half_width = width // 2
89
+ half_height = height // 2
90
+ # create coordinates (top-left x, top-left y, bottom-right x, bottom-right y)
91
+ coordinates = [(0, 0, half_width, half_height),
92
+ (half_width, 0, width, half_height),
93
+ (0, half_height, half_width, height),
94
+ (half_width, half_height, width, height)]
95
+
96
+ images = [img.crop(c) for c in coordinates]
97
+ return images
98
+
99
+ def auth_mj(self):
100
+ """
101
+ auth midjourney api
102
+ """
103
+ # TODO: check if secret is valid
104
+ return {'status': 'ok'}
105
+
106
+ def request_mj(self, path: str, action: str, data: str, retries=3):
107
+ """
108
+ request midjourney api
109
+ """
110
+ mj_proxy_url = self.proxy_url
111
+ if mj_proxy_url is None or not (mj_proxy_url.startswith("http://") or mj_proxy_url.startswith("https://")):
112
+ raise Exception('please set MIDJOURNEY_PROXY_API_BASE in ENV or in config.json')
113
+
114
+ auth_ = self.auth_mj()
115
+ if auth_.get('error'):
116
+ raise Exception('auth not set')
117
+
118
+ fetch_url = f"{mj_proxy_url}/{path}"
119
+ # logging.info(f"[MJ Proxy] {action} {fetch_url} params: {data}")
120
+
121
+ for _ in range(retries):
122
+ try:
123
+ with retrieve_proxy():
124
+ res = requests.request(method=action, url=fetch_url, headers=self.headers, data=data)
125
+ break
126
+ except Exception as e:
127
+ print(e)
128
+
129
+ if res.status_code != 200:
130
+ raise Exception(f'{res.status_code} - {res.content}')
131
+
132
+ return res
133
+
134
+ def fetch_status(self, fetch_data: FetchDataPack):
135
+ """
136
+ fetch status of current task
137
+ """
138
+ if fetch_data.start_time + fetch_data.timeout < time.time():
139
+ fetch_data.finished = True
140
+ return "任务超时,请检查 dc 输出。描述:" + fetch_data.prompt
141
+
142
+ time.sleep(3)
143
+ status_res = self.request_mj(f"task/{fetch_data.task_id}/fetch", "GET", '')
144
+ status_res_json = status_res.json()
145
+ if not (200 <= status_res.status_code < 300):
146
+ raise Exception("任务状态获取失败:" + status_res_json.get(
147
+ 'error') or status_res_json.get('description') or '未知错误')
148
+ else:
149
+ fetch_data.finished = False
150
+ if status_res_json['status'] == "SUCCESS":
151
+ content = status_res_json['imageUrl']
152
+ fetch_data.finished = True
153
+ elif status_res_json['status'] == "FAILED":
154
+ content = status_res_json['failReason'] or '未知原因'
155
+ fetch_data.finished = True
156
+ elif status_res_json['status'] == "NOT_START":
157
+ content = f'任务未开始,已等待 {time.time() - fetch_data.start_time:.2f} 秒'
158
+ elif status_res_json['status'] == "IN_PROGRESS":
159
+ content = '任务正在运行'
160
+ if status_res_json.get('progress'):
161
+ content += f",进度:{status_res_json['progress']}"
162
+ elif status_res_json['status'] == "SUBMITTED":
163
+ content = '任务已提交处理'
164
+ elif status_res_json['status'] == "FAILURE":
165
+ fetch_data.finished = True
166
+ return "任务处理失败,原因:" + status_res_json['failReason'] or '未知原因'
167
+ else:
168
+ content = status_res_json['status']
169
+ if fetch_data.finished:
170
+ img_url = self.use_mj_self_proxy_url(status_res_json['imageUrl'])
171
+ if fetch_data.action == "DESCRIBE":
172
+ return f"\n{status_res_json['prompt']}"
173
+ time_cost_str = f"\n\n{fetch_data.action} 花费时间:{time.time() - fetch_data.start_time:.2f} 秒"
174
+ upscale_str = ""
175
+ variation_str = ""
176
+ if fetch_data.action in ["IMAGINE", "UPSCALE", "VARIATION"]:
177
+ upscale = [f'/mj UPSCALE{self.command_splitter}{i+1}{self.command_splitter}{fetch_data.task_id}'
178
+ for i in range(4)]
179
+ upscale_str = '\n放大图片:\n\n' + '\n\n'.join(upscale)
180
+ variation = [f'/mj VARIATION{self.command_splitter}{i+1}{self.command_splitter}{fetch_data.task_id}'
181
+ for i in range(4)]
182
+ variation_str = '\n图片变体:\n\n' + '\n\n'.join(variation)
183
+ if self.temp_path and fetch_data.action in ["IMAGINE", "VARIATION"]:
184
+ try:
185
+ images = self.split_image(img_url)
186
+ # save images to temp path
187
+ for i in range(4):
188
+ images[i].save(pathlib.Path(self.temp_path) / f"{fetch_data.task_id}_{i}.png")
189
+ img_str = '\n'.join(
190
+ [f"![{fetch_data.task_id}](/file={self.temp_path}/{fetch_data.task_id}_{i}.png)"
191
+ for i in range(4)])
192
+ return fetch_data.prefix_content + f"{time_cost_str}\n\n{img_str}{upscale_str}{variation_str}"
193
+ except Exception as e:
194
+ logging.error(e)
195
+ return fetch_data.prefix_content + \
196
+ f"{time_cost_str}[![{fetch_data.task_id}]({img_url})]({img_url}){upscale_str}{variation_str}"
197
+ else:
198
+ content = f"**任务状态:** [{(datetime.now()).strftime('%Y-%m-%d %H:%M:%S')}] - {content}"
199
+ content += f"\n\n花费时间:{time.time() - fetch_data.start_time:.2f} 秒"
200
+ if status_res_json['status'] == 'IN_PROGRESS' and status_res_json.get('imageUrl'):
201
+ img_url = status_res_json.get('imageUrl')
202
+ return f"{content}\n[![{fetch_data.task_id}]({img_url})]({img_url})"
203
+ return content
204
+ return None
205
+
206
+ def handle_file_upload(self, files, chatbot, language):
207
+ """
208
+ handle file upload
209
+ """
210
+ if files:
211
+ for file in files:
212
+ if file.name:
213
+ logging.info(f"尝试读取图像: {file.name}")
214
+ self.try_read_image(file.name)
215
+ if self.image_path is not None:
216
+ chatbot = chatbot + [((self.image_path,), None)]
217
+ if self.image_bytes is not None:
218
+ logging.info("使用图片作为输入")
219
+ return None, chatbot, None
220
+
221
+ def reset(self):
222
+ self.image_bytes = None
223
+ self.image_path = None
224
+ return [], "已重置"
225
+
226
+ def get_answer_at_once(self):
227
+ content = self.history[-1]['content']
228
+ answer = self.get_help()
229
+
230
+ if not content.lower().startswith("/mj"):
231
+ return answer, len(content)
232
+
233
+ prompt = content[3:].strip()
234
+ action = "IMAGINE"
235
+ first_split_index = prompt.find(self.command_splitter)
236
+ if first_split_index > 0:
237
+ action = prompt[:first_split_index]
238
+ if action not in ["IMAGINE", "DESCRIBE", "UPSCALE",
239
+ # "VARIATION", "BLEND", "REROLL"
240
+ ]:
241
+ raise Exception("任务提交失败:未知的任务类型")
242
+ else:
243
+ action_index = None
244
+ action_use_task_id = None
245
+ if action in ["VARIATION", "UPSCALE", "REROLL"]:
246
+ action_index = int(prompt[first_split_index + 2:first_split_index + 3])
247
+ action_use_task_id = prompt[first_split_index + 5:]
248
+
249
+ try:
250
+ res = None
251
+ if action == "IMAGINE":
252
+ data = {
253
+ "prompt": prompt
254
+ }
255
+ if self.image_bytes is not None:
256
+ data["base64"] = 'data:image/png;base64,' + self.image_bytes
257
+ res = self.request_mj("submit/imagine", "POST",
258
+ json.dumps(data))
259
+ elif action == "DESCRIBE":
260
+ res = self.request_mj("submit/describe", "POST",
261
+ json.dumps({"base64": 'data:image/png;base64,' + self.image_bytes}))
262
+ elif action == "BLEND":
263
+ res = self.request_mj("submit/blend", "POST", json.dumps(
264
+ {"base64Array": [self.image_bytes, self.image_bytes]}))
265
+ elif action in ["UPSCALE", "VARIATION", "REROLL"]:
266
+ res = self.request_mj(
267
+ "submit/change", "POST",
268
+ json.dumps({"action": action, "index": action_index, "taskId": action_use_task_id}))
269
+ res_json = res.json()
270
+ if not (200 <= res.status_code < 300) or (res_json['code'] not in [1, 22]):
271
+ answer = "任务提交失败:" + res_json.get('error', res_json.get('description', '未知错误'))
272
+ else:
273
+ task_id = res_json['result']
274
+ prefix_content = f"**画面描述:** {prompt}\n**任务ID:** {task_id}\n"
275
+
276
+ fetch_data = Midjourney_Client.FetchDataPack(
277
+ action=action,
278
+ prefix_content=prefix_content,
279
+ task_id=task_id,
280
+ )
281
+ fetch_data.prompt = prompt
282
+ while not fetch_data.finished:
283
+ answer = self.fetch_status(fetch_data)
284
+ except Exception as e:
285
+ logging.error("submit failed", e)
286
+ answer = "任务提交错误:" + str(e.args[0]) if e.args else '未知错误'
287
+
288
+ return answer, tiktoken.get_encoding("cl100k_base").encode(content)
289
+
290
+ def get_answer_stream_iter(self):
291
+ content = self.history[-1]['content']
292
+ answer = self.get_help()
293
+
294
+ if not content.lower().startswith("/mj"):
295
+ yield answer
296
+ return
297
+
298
+ prompt = content[3:].strip()
299
+ action = "IMAGINE"
300
+ first_split_index = prompt.find(self.command_splitter)
301
+ if first_split_index > 0:
302
+ action = prompt[:first_split_index]
303
+ if action not in ["IMAGINE", "DESCRIBE", "UPSCALE",
304
+ "VARIATION", "BLEND", "REROLL"
305
+ ]:
306
+ yield "任务提交失败:未知的任务类型"
307
+ return
308
+
309
+ action_index = None
310
+ action_use_task_id = None
311
+ if action in ["VARIATION", "UPSCALE", "REROLL"]:
312
+ action_index = int(prompt[first_split_index + 2:first_split_index + 3])
313
+ action_use_task_id = prompt[first_split_index + 5:]
314
+
315
+ try:
316
+ res = None
317
+ if action == "IMAGINE":
318
+ data = {
319
+ "prompt": prompt
320
+ }
321
+ if self.image_bytes is not None:
322
+ data["base64"] = 'data:image/png;base64,' + self.image_bytes
323
+ res = self.request_mj("submit/imagine", "POST",
324
+ json.dumps(data))
325
+ elif action == "DESCRIBE":
326
+ res = self.request_mj("submit/describe", "POST", json.dumps(
327
+ {"base64": 'data:image/png;base64,' + self.image_bytes}))
328
+ elif action == "BLEND":
329
+ res = self.request_mj("submit/blend", "POST", json.dumps(
330
+ {"base64Array": [self.image_bytes, self.image_bytes]}))
331
+ elif action in ["UPSCALE", "VARIATION", "REROLL"]:
332
+ res = self.request_mj(
333
+ "submit/change", "POST",
334
+ json.dumps({"action": action, "index": action_index, "taskId": action_use_task_id}))
335
+ res_json = res.json()
336
+ if not (200 <= res.status_code < 300) or (res_json['code'] not in [1, 22]):
337
+ yield "任务提交失败:" + res_json.get('error', res_json.get('description', '未知错误'))
338
+ else:
339
+ task_id = res_json['result']
340
+ prefix_content = f"**画面描述:** {prompt}\n**任务ID:** {task_id}\n"
341
+ content = f"[{(datetime.now()).strftime('%Y-%m-%d %H:%M:%S')}] - 任务提交成功:" + \
342
+ res_json.get('description') or '请稍等片刻'
343
+ yield content
344
+
345
+ fetch_data = Midjourney_Client.FetchDataPack(
346
+ action=action,
347
+ prefix_content=prefix_content,
348
+ task_id=task_id,
349
+ )
350
+ while not fetch_data.finished:
351
+ yield self.fetch_status(fetch_data)
352
+ except Exception as e:
353
+ logging.error('submit failed', e)
354
+ yield "任务提交错误:" + str(e.args[0]) if e.args else '未知错误'
355
+
356
+ def get_help(self):
357
+ return """```
358
+ 【绘图帮助】
359
+ 所有命令都需要以 /mj 开头,如:/mj a dog
360
+ IMAGINE - 绘图,可以省略该命令,后面跟上绘图内容
361
+ /mj a dog
362
+ /mj IMAGINE::a cat
363
+ DESCRIBE - 描述图片,需要在右下角上传需要描述的图片内容
364
+ /mj DESCRIBE::
365
+ UPSCALE - 确认后放大图片,第一个数值为需要放大的图片(1~4),第二参数为任务ID
366
+ /mj UPSCALE::1::123456789
367
+ 请使用SD进行UPSCALE
368
+ VARIATION - 图片变体,第一个数值为需要放大的图片(1~4),第二参数为任务ID
369
+ /mj VARIATION::1::123456789
370
+
371
+ 【绘图参数】
372
+ 所有命令默认会带上参数--v 5.2
373
+ 其他参数参照 https://docs.midjourney.com/docs/parameter-list
374
+ 长宽比 --aspect/--ar
375
+ --ar 1:2
376
+ --ar 16:9
377
+ 负面tag --no
378
+ --no plants
379
+ --no hands
380
+ 随机种子 --seed
381
+ --seed 1
382
+ 生成动漫风格(NijiJourney) --niji
383
+ --niji
384
+ ```
385
+ """
modules/models/models.py CHANGED
@@ -621,6 +621,10 @@ def get_model(
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.Unknown:
625
  raise ValueError(f"未知模型: {model_name}")
626
  logging.info(msg)
 
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)
modules/presets.py CHANGED
@@ -69,6 +69,7 @@ ONLINE_MODELS = [
69
  "yuanai-1.0-rhythm_poems",
70
  "minimax-abab4-chat",
71
  "minimax-abab5-chat",
 
72
  ]
73
 
74
  LOCAL_MODELS = [
@@ -242,6 +243,7 @@ small_and_beautiful_theme = gr.themes.Soft(
242
  block_title_background_fill_dark="*primary_900",
243
  block_label_background_fill_dark="*primary_900",
244
  input_background_fill="#F6F6F6",
245
- chatbot_code_background_color="*neutral_950",
 
246
  chatbot_code_background_color_dark="*neutral_950",
247
  )
 
69
  "yuanai-1.0-rhythm_poems",
70
  "minimax-abab4-chat",
71
  "minimax-abab5-chat",
72
+ "midjourney"
73
  ]
74
 
75
  LOCAL_MODELS = [
 
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
+ # gradio 会把这个几个chatbot打头的变量应用到其他md渲染的地方,鬼晓得怎么想的。。。
248
  chatbot_code_background_color_dark="*neutral_950",
249
  )
modules/train_func.py CHANGED
@@ -138,7 +138,7 @@ def add_to_models():
138
  extra_models = [job["fine_tuned_model"] for job in succeeded_jobs]
139
  for i in extra_models:
140
  if i not in presets.MODELS:
141
- presets.MODELS.append(extra_models)
142
 
143
  with open('config.json', 'r') as f:
144
  data = commentjson.load(f)
 
138
  extra_models = [job["fine_tuned_model"] for job in succeeded_jobs]
139
  for i in extra_models:
140
  if i not in presets.MODELS:
141
+ presets.MODELS.append(i)
142
 
143
  with open('config.json', 'r') as f:
144
  data = commentjson.load(f)
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.173
11
  markdown
12
  PyPDF2
13
  pdfplumber
 
1
+ gradio==3.41.2
2
+ gradio_client==0.5.0
3
  pypinyin
4
  tiktoken
5
  socksio
 
7
  colorama
8
  googlesearch-python
9
  Pygments
10
+ langchain==0.0.276
11
  markdown
12
  PyPDF2
13
  pdfplumber
web_assets/html/config_info.html CHANGED
@@ -1,2 +1,2 @@
1
- <div id="config-bot-avatar-url">{bot_avatar}</div>
2
- <div id="config-user-avatar-url">{user_avatar}</div>
 
1
+
2
+ <!-- removed -->
web_assets/javascript/ChuanhuChat.js CHANGED
@@ -37,26 +37,28 @@ var toolbox = null;
37
 
38
  var isInIframe = (window.self !== window.top);
39
  var currentTime = new Date().getTime();
40
- var initialized = false;
41
 
42
  let windowWidth = window.innerWidth; // 初始窗口宽度
43
 
44
- // gradio 页面加载好了么??? 我能动你的元素了么??
45
- function gradioLoaded(mutations) {
46
- for (var i = 0; i < mutations.length; i++) {
47
- if (mutations[i].addedNodes.length) {
48
- if (initialized) {
49
- observer.disconnect(); // 停止监听
50
- return;
51
- }
52
- initialize();
53
  }
54
  }
 
 
 
 
55
  }
56
 
57
  function initialize() {
58
- var needInit = {gradioContainer, apSwitch, user_input_tb, userInfoDiv, appTitleDiv, chatbot, chatbotIndicator, chatbotWrap, statusDisplay, sliders, updateChuanhuBtn, chuanhuPopup};
59
- initialized = true;
60
 
61
  loginUserForm = gradioApp().querySelector(".gradio-container > .main > .wrap > .panel > .form")
62
  gradioContainer = gradioApp().querySelector(".gradio-container");
@@ -64,7 +66,6 @@ function initialize() {
64
  userInfoDiv = gradioApp().getElementById("user-info");
65
  appTitleDiv = gradioApp().getElementById("app-title");
66
  chatbot = gradioApp().querySelector('#chuanhu-chatbot');
67
- chatbotIndicator = gradioApp().querySelector('#chuanhu-chatbot>div.wrap');
68
  chatbotWrap = gradioApp().querySelector('#chuanhu-chatbot > .wrapper > .wrap');
69
  apSwitch = gradioApp().querySelector('.apSwitch input[type="checkbox"]');
70
  updateToast = gradioApp().querySelector("#toast-update");
@@ -89,38 +90,29 @@ function initialize() {
89
  userLogged = true;
90
  }
91
 
92
- for (let elem in needInit) {
93
- if (needInit[elem] == null) {
94
- initialized = false;
95
- return;
96
- }
97
- }
 
 
98
 
99
- if (initialized) {
100
- adjustDarkMode();
101
- selectHistory();
102
- setTimeout(showOrHideUserInfo(), 2000);
103
- setChatbotHeight();
104
- setChatbotScroll();
105
- setPopupBoxPosition();
106
- setChatAreaWidth();
107
- setSlider();
108
- setAvatar();
109
- if (!historyLoaded) loadHistoryHtml();
110
- if (!usernameGotten) getUserInfo();
111
- chatbotObserver.observe(chatbotIndicator, { attributes: true });
112
-
113
- const lastCheckTime = localStorage.getItem('lastCheckTime') || 0;
114
- const longTimeNoCheck = currentTime - lastCheckTime > 3 * 24 * 60 * 60 * 1000;
115
- if (longTimeNoCheck && !updateInfoGotten && !isLatestVersion || isLatestVersion && !updateInfoGotten) {
116
- updateLatestVersion();
117
- }
118
 
119
- // setHistroyPanel();
120
- settingBox.classList.add('hideBox');
121
- trainingBox.classList.add('hideBox');
122
- // trainBody.classList.add('hide-body');
123
  }
 
 
 
 
 
 
124
  }
125
 
126
  function gradioApp() {
@@ -283,42 +275,19 @@ function setChatbotScroll() {
283
  chatbotWrap.scrollTo(0,scrollHeight)
284
  }
285
 
286
- var botAvatarUrl = "";
287
- var userAvatarUrl = "";
288
- function setAvatar() {
289
- var botAvatar = gradioApp().getElementById("config-bot-avatar-url").innerText;
290
- var userAvatar = gradioApp().getElementById("config-user-avatar-url").innerText;
291
-
292
- if (botAvatar == "none") {
293
- botAvatarUrl = "";
294
- } else if (isImgUrl(botAvatar)) {
295
- botAvatarUrl = botAvatar;
296
- } else {
297
- // botAvatarUrl = "https://github.com/GaiZhenbiao/ChuanhuChatGPT/assets/70903329/aca3a7ec-4f1d-4667-890c-a6f47bf08f63";
298
- botAvatarUrl = "/file=web_assets/chatbot.png"
299
- }
300
-
301
- if (userAvatar == "none") {
302
- userAvatarUrl = "";
303
- } else if (isImgUrl(userAvatar)) {
304
- userAvatarUrl = userAvatar;
305
- } else {
306
- 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";
307
- }
308
- }
309
-
310
  function clearChatbot() {
311
  clearHistoryHtml();
312
- clearMessageRows();
313
  }
314
 
315
  function chatbotContentChanged(attempt = 1) {
316
  for (var i = 0; i < attempt; i++) {
317
  setTimeout(() => {
 
318
  saveHistoryHtml();
319
  disableSendBtn();
320
- gradioApp().querySelectorAll('#chuanhu-chatbot .message-wrap .message.user').forEach((userElement) => {addAvatars(userElement, 'user')});
321
- gradioApp().querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach((botElement) => {addAvatars(botElement, 'bot'); addChuanhuButton(botElement)});
322
  }, i === 0 ? 0 : 500);
323
  }
324
  // 理论上是不需要多次尝试执行的,可惜gradio的bug导致message可能没有渲染完毕,所以尝试500ms后再次执行
@@ -332,14 +301,21 @@ var chatbotObserver = new MutationObserver(() => {
332
  });
333
 
334
  // 监视页面内部 DOM 变动
335
- var observer = new MutationObserver(function (mutations) {
336
- gradioLoaded(mutations);
 
 
 
 
 
 
 
337
  });
338
 
339
  // 监视页面变化
340
  window.addEventListener("DOMContentLoaded", function () {
341
- const ga = document.getElementsByTagName("gradio-app");
342
- observer.observe(ga[0], { childList: true, subtree: true });
343
  isInIframe = (window.self !== window.top);
344
  historyLoaded = false;
345
  });
 
37
 
38
  var isInIframe = (window.self !== window.top);
39
  var currentTime = new Date().getTime();
 
40
 
41
  let windowWidth = window.innerWidth; // 初始窗口宽度
42
 
43
+ function addInit() {
44
+ var needInit = {chatbotIndicator};
45
+
46
+ chatbotIndicator = gradioApp().querySelector('#chuanhu-chatbot > div.wrap');
47
+
48
+ for (let elem in needInit) {
49
+ if (needInit[elem] == null) {
50
+ // addInited = false;
51
+ return false;
52
  }
53
  }
54
+
55
+ chatbotObserver.observe(chatbotIndicator, { attributes: true });
56
+
57
+ return true;
58
  }
59
 
60
  function initialize() {
61
+ gradioObserver.observe(gradioApp(), { childList: true, subtree: true });
 
62
 
63
  loginUserForm = gradioApp().querySelector(".gradio-container > .main > .wrap > .panel > .form")
64
  gradioContainer = gradioApp().querySelector(".gradio-container");
 
66
  userInfoDiv = gradioApp().getElementById("user-info");
67
  appTitleDiv = gradioApp().getElementById("app-title");
68
  chatbot = gradioApp().querySelector('#chuanhu-chatbot');
 
69
  chatbotWrap = gradioApp().querySelector('#chuanhu-chatbot > .wrapper > .wrap');
70
  apSwitch = gradioApp().querySelector('.apSwitch input[type="checkbox"]');
71
  updateToast = gradioApp().querySelector("#toast-update");
 
90
  userLogged = true;
91
  }
92
 
93
+ adjustDarkMode();
94
+ selectHistory();
95
+ setTimeout(showOrHideUserInfo(), 2000);
96
+ setChatbotHeight();
97
+ setChatbotScroll();
98
+ setPopupBoxPosition();
99
+ setChatAreaWidth();
100
+ setSlider();
101
 
102
+ if (!historyLoaded) loadHistoryHtml();
103
+ if (!usernameGotten) getUserInfo();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
+ const lastCheckTime = localStorage.getItem('lastCheckTime') || 0;
106
+ const longTimeNoCheck = currentTime - lastCheckTime > 3 * 24 * 60 * 60 * 1000;
107
+ if (longTimeNoCheck && !updateInfoGotten && !isLatestVersion || isLatestVersion && !updateInfoGotten) {
108
+ updateLatestVersion();
109
  }
110
+
111
+ // setHistroyPanel();
112
+ settingBox.classList.add('hideBox');
113
+ trainingBox.classList.add('hideBox');
114
+ // trainBody.classList.add('hide-body');
115
+ return true;
116
  }
117
 
118
  function gradioApp() {
 
275
  chatbotWrap.scrollTo(0,scrollHeight)
276
  }
277
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  function clearChatbot() {
279
  clearHistoryHtml();
280
+ // clearMessageRows();
281
  }
282
 
283
  function chatbotContentChanged(attempt = 1) {
284
  for (var i = 0; i < attempt; i++) {
285
  setTimeout(() => {
286
+ // clearMessageRows();
287
  saveHistoryHtml();
288
  disableSendBtn();
289
+ // gradioApp().querySelectorAll('#chuanhu-chatbot .message-wrap .message.user').forEach((userElement) => {addAvatars(userElement, 'user')});
290
+ gradioApp().querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach(addChuanhuButton);
291
  }, i === 0 ? 0 : 500);
292
  }
293
  // 理论上是不需要多次尝试执行的,可惜gradio的bug导致message可能没有渲染完毕,所以尝试500ms后再次执行
 
301
  });
302
 
303
  // 监视页面内部 DOM 变动
304
+ var gradioObserver = new MutationObserver(function (mutations) {
305
+ for (var i = 0; i < mutations.length; i++) {
306
+ if (mutations[i].addedNodes.length) {
307
+ if (addInit()) {
308
+ gradioObserver.disconnect();
309
+ return;
310
+ }
311
+ }
312
+ }
313
  });
314
 
315
  // 监视页面变化
316
  window.addEventListener("DOMContentLoaded", function () {
317
+ // const ga = document.getElementsByTagName("gradio-app");
318
+ gradioApp().addEventListener("render", initialize);
319
  isInIframe = (window.self !== window.top);
320
  historyLoaded = false;
321
  });
web_assets/javascript/avatar.js DELETED
@@ -1,48 +0,0 @@
1
-
2
- function addAvatars(messageElement, role='user'||'bot') {
3
- if (messageElement.classList.contains('avatar-added') || messageElement.classList.contains('hide')) {
4
- return;
5
- }
6
- if (role === 'bot' && botAvatarUrl === "" || role === 'user' && userAvatarUrl === "") {
7
- messageElement.classList.add('avatar-added');
8
- return;
9
- }
10
-
11
-
12
- const messageRow = document.createElement('div');
13
- messageRow.classList.add('message-row');
14
- messageElement.classList.add('avatar-added');
15
-
16
- if (role === 'bot') {
17
- messageRow.classList.add('bot-message-row');
18
- } else if (role === 'user') {
19
- messageRow.classList.add('user-message-row');
20
- }
21
-
22
- const avatarDiv = document.createElement('div');
23
- avatarDiv.classList.add('chatbot-avatar');
24
- if (role === 'bot') {
25
- avatarDiv.classList.add('bot-avatar');
26
- avatarDiv.innerHTML = `<img src="${botAvatarUrl}" alt="bot-avatar" />`;
27
- } else if (role === 'user') {
28
- avatarDiv.classList.add('user-avatar');
29
- avatarDiv.innerHTML = `<img src="${userAvatarUrl}" alt="user-avatar" />`;
30
- }
31
-
32
- messageElement.parentNode.replaceChild(messageRow, messageElement);
33
-
34
- if (role === 'bot') {
35
- messageRow.appendChild(avatarDiv);
36
- messageRow.appendChild(messageElement);
37
- } else if (role === 'user') {
38
- messageRow.appendChild(messageElement);
39
- messageRow.appendChild(avatarDiv);
40
- }
41
- }
42
-
43
- function clearMessageRows() {
44
- const messageRows = chatbotWrap.querySelectorAll('.message-row');
45
- messageRows.forEach((messageRow) => {
46
- messageRow.parentNode.removeChild(messageRow);
47
- });
48
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
web_assets/javascript/chat-history.js CHANGED
@@ -25,6 +25,12 @@ function loadHistoryHtml() {
25
  return; // logged in, do nothing
26
  }
27
  if (!historyLoaded) {
 
 
 
 
 
 
28
  var fakeHistory = document.createElement('div');
29
  fakeHistory.classList.add('history-message');
30
  fakeHistory.innerHTML = tempDiv.innerHTML;
 
25
  return; // logged in, do nothing
26
  }
27
  if (!historyLoaded) {
28
+ // preprocess, gradio buttons in history lost their event listeners
29
+ var gradioCopyButtons = tempDiv.querySelectorAll('button.copy_code_button');
30
+ for (var i = 0; i < gradioCopyButtons.length; i++) {
31
+ gradioCopyButtons[i].parentNode.removeChild(gradioCopyButtons[i]);
32
+ }
33
+
34
  var fakeHistory = document.createElement('div');
35
  fakeHistory.classList.add('history-message');
36
  fakeHistory.innerHTML = tempDiv.innerHTML;
web_assets/stylesheet/ChuanhuChat.css CHANGED
@@ -47,6 +47,7 @@ body.popup-open {
47
  }
48
 
49
  #netsetting-warning hr {
 
50
  margin-bottom: 1em;
51
  }
52
 
 
47
  }
48
 
49
  #netsetting-warning hr {
50
+ margin-top: 0.5em;
51
  margin-bottom: 1em;
52
  }
53
 
web_assets/stylesheet/chatbot.css CHANGED
@@ -84,13 +84,16 @@ hr.append-display {
84
  min-width: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
85
  }
86
  [data-testid = "bot"] {
87
- max-width: calc(85% - 38px);
88
- border-top-left-radius: 0 !important;
89
  }
90
  [data-testid = "user"] {
91
- max-width: calc(85% - 38px);
92
  width: auto !important;
93
- border-top-right-radius: 0 !important;
 
 
 
94
  }
95
 
96
  /* 屏幕宽度大于等于500px的设备 */
@@ -200,6 +203,10 @@ hr.append-display {
200
  .message div.icon-button > button[title="copy"] {
201
  display: none;
202
  }
 
 
 
 
203
 
204
 
205
  /* history message */
@@ -244,35 +251,12 @@ hr.append-display {
244
  note: find it better without transition animation...;
245
  } */
246
 
247
-
248
- .message-row {
249
- flex-direction: row;
250
- display: flex;
251
- gap: 8px;
252
- width: 100%;
253
- }
254
- .bot-message-row {
255
- justify-content: flex-start;
256
- }
257
- .user-message-row {
258
- justify-content: flex-end;
259
  }
260
- .chatbot-avatar {
261
- width: 32px;
262
- height: 32px;
263
  background-color: transparent;
264
  background-size: cover;
265
- border-radius: 5px !important;
266
  }
267
- .chatbot-avatar.bot-avatar {
268
- margin-left: 5px;
269
- }
270
- .chatbot-avatar.user-avatar {
271
- margin-right: 10px;
272
- }
273
- .chatbot-avatar img {
274
- border-radius: 5px !important;
275
- object-fit: cover;
276
- width: 100%;
277
- height: 100%;
278
- }
 
84
  min-width: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
85
  }
86
  [data-testid = "bot"] {
87
+ max-width: calc(85% - 40px);
88
+ border-bottom-left-radius: 0 !important;
89
  }
90
  [data-testid = "user"] {
91
+ max-width: calc(85% - 40px);
92
  width: auto !important;
93
+ border-bottom-right-radius: 0 !important;
94
+ }
95
+ .message-row.user-row {
96
+ justify-content: flex-end;
97
  }
98
 
99
  /* 屏幕宽度大于等于500px的设备 */
 
203
  .message div.icon-button > button[title="copy"] {
204
  display: none;
205
  }
206
+ /* disable share button and other buttons in hugging face spaces */
207
+ #chuanhu-chatbot > .wrapper > .icon-button {
208
+ display: none !important;
209
+ }
210
 
211
 
212
  /* history message */
 
251
  note: find it better without transition animation...;
252
  } */
253
 
254
+ img.avatar-image {
255
+ border-radius: 5px !important;
 
 
 
 
 
 
 
 
 
 
256
  }
257
+ .avatar-container {
258
+ width: 32px !important;
259
+ height: 32px !important;
260
  background-color: transparent;
261
  background-size: cover;
 
262
  }
 
 
 
 
 
 
 
 
 
 
 
 
web_assets/stylesheet/markdown.css CHANGED
@@ -4,52 +4,57 @@
4
  }
5
 
6
  /* 表格 */
7
- .message table {
8
  margin: 1em 0;
9
  border-collapse: collapse;
10
  empty-cells: show;
11
  }
12
- .message td, .message th {
13
  border: 1.2px solid var(--border-color-primary) !important;
14
  padding: 0.2em;
15
  }
16
- .message thead {
17
  background-color: rgba(175,184,193,0.2);
18
  }
19
- .message thead th {
20
  padding: .5em .2em;
21
  }
22
 
23
  /* 行内代码 */
24
- .message :not(pre) code {
25
  display: inline;
26
  white-space: break-spaces;
27
- font-family: var(--font-mono);
28
- border-radius: 6px;
29
  margin: 0 2px 0 2px;
30
- padding: .2em .4em .1em .4em;
31
- background-color: rgba(175,184,193,0.2);
 
 
32
  }
33
  /* 代码块 */
34
- .message pre,
35
- .message pre[class*=language-] {
36
  color: #fff;
37
  overflow-x: auto;
38
  overflow-y: hidden;
39
- margin: .8em 1em 1em 0em !important;
40
  padding: var(--spacing-xl) 1.2em !important;
41
  border-radius: var(--radius-lg) !important;
 
42
  }
43
- .message pre code,
44
- .message pre code[class*=language-] {
45
  color: #fff;
46
  padding: 0;
47
  margin: 0;
48
  background-color: unset;
49
  text-shadow: none;
50
  font-family: var(--font-mono);
 
 
 
 
51
  }
52
-
53
 
54
  /* 覆盖prism.css */
55
  .language-css .token.string,
 
4
  }
5
 
6
  /* 表格 */
7
+ .md-message table {
8
  margin: 1em 0;
9
  border-collapse: collapse;
10
  empty-cells: show;
11
  }
12
+ .md-message td, .message th {
13
  border: 1.2px solid var(--border-color-primary) !important;
14
  padding: 0.2em;
15
  }
16
+ .md-message thead {
17
  background-color: rgba(175,184,193,0.2);
18
  }
19
+ .md-message thead th {
20
  padding: .5em .2em;
21
  }
22
 
23
  /* 行内代码 */
24
+ .md-message :not(pre) > code {
25
  display: inline;
26
  white-space: break-spaces;
27
+ font-family: var(--font-mono) !important;
28
+ border-radius: 6px !important;
29
  margin: 0 2px 0 2px;
30
+ padding: .1em .4em .08em .4em !important;
31
+ background-color: rgba(175,184,193,0.2) !important;
32
+ border: none !important;
33
+ font-size: var(--text-md) !important;
34
  }
35
  /* 代码块 */
36
+ .md-message pre,
37
+ .md-message pre[class*=language-] {
38
  color: #fff;
39
  overflow-x: auto;
40
  overflow-y: hidden;
 
41
  padding: var(--spacing-xl) 1.2em !important;
42
  border-radius: var(--radius-lg) !important;
43
+ background: var(--neutral-950) !important;
44
  }
45
+ .md-message pre code,
46
+ .md-message pre code[class*=language-] {
47
  color: #fff;
48
  padding: 0;
49
  margin: 0;
50
  background-color: unset;
51
  text-shadow: none;
52
  font-family: var(--font-mono);
53
+ font-size: var(--text-md);
54
+ }
55
+ .md-message .code_wrap {
56
+ margin: .8em 1em 1em 0em;
57
  }
 
58
 
59
  /* 覆盖prism.css */
60
  .language-css .token.string,
web_assets/stylesheet/override-gradio.css CHANGED
@@ -15,14 +15,15 @@ footer {
15
  }
16
 
17
  /* 覆盖 gradio 丑陋的复制按钮样式 */
18
- .message pre button[title="copy"] {
19
- border-radius: 5px;
20
  transition: background-color .2s ease;
 
21
  }
22
- .message pre button[title="copy"]:hover {
23
  background-color: #333232;
24
  }
25
- .message pre button .check {
26
  color: #fff !important;
27
  background: var(--neutral-950) !important;
28
  }
 
15
  }
16
 
17
  /* 覆盖 gradio 丑陋的复制按钮样式 */
18
+ .message .code_wrap button[title="copy"] {
19
+ border-radius: 5px !important;
20
  transition: background-color .2s ease;
21
+ color: white;
22
  }
23
+ .message .code_wrap button[title="copy"]:hover {
24
  background-color: #333232;
25
  }
26
+ .message .code_wrap button .check {
27
  color: #fff !important;
28
  background: var(--neutral-950) !important;
29
  }
web_assets/user.png ADDED