Spaces:
Sleeping
Sleeping
Merge branch 'main' into UI-new
Browse files- .gitignore +1 -0
- ChuanhuChatbot.py +2 -2
- config_example.json +7 -2
- modules/config.py +18 -1
- modules/models/base_model.py +4 -1
- modules/models/midjourney.py +385 -0
- modules/models/models.py +4 -0
- modules/presets.py +3 -1
- modules/train_func.py +1 -1
- requirements.txt +3 -3
- web_assets/html/config_info.html +2 -2
- web_assets/javascript/ChuanhuChat.js +49 -73
- web_assets/javascript/avatar.js +0 -48
- web_assets/javascript/chat-history.js +6 -0
- web_assets/stylesheet/ChuanhuChat.css +1 -0
- web_assets/stylesheet/chatbot.css +16 -32
- web_assets/stylesheet/markdown.css +20 -15
- web_assets/stylesheet/override-gradio.css +5 -4
- web_assets/user.png +0 -0
.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(
|
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", //
|
28 |
-
"user_avatar": "default", //
|
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 "
|
|
|
|
|
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(
|
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.
|
2 |
-
gradio_client==0.
|
3 |
pypinyin
|
4 |
tiktoken
|
5 |
socksio
|
@@ -7,7 +7,7 @@ tqdm
|
|
7 |
colorama
|
8 |
googlesearch-python
|
9 |
Pygments
|
10 |
-
langchain==0.0.
|
11 |
markdown
|
12 |
PyPDF2
|
13 |
pdfplumber
|
|
|
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 |
-
|
2 |
-
|
|
|
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 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
}
|
54 |
}
|
|
|
|
|
|
|
|
|
55 |
}
|
56 |
|
57 |
function initialize() {
|
58 |
-
|
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 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
|
|
|
|
98 |
|
99 |
-
if (
|
100 |
-
|
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 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
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(
|
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
|
336 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
337 |
});
|
338 |
|
339 |
// 监视页面变化
|
340 |
window.addEventListener("DOMContentLoaded", function () {
|
341 |
-
const ga = document.getElementsByTagName("gradio-app");
|
342 |
-
|
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% -
|
88 |
-
border-
|
89 |
}
|
90 |
[data-testid = "user"] {
|
91 |
-
max-width: calc(85% -
|
92 |
width: auto !important;
|
93 |
-
border-
|
|
|
|
|
|
|
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 |
-
|
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 |
-
.
|
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: .
|
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
|
19 |
-
border-radius: 5px;
|
20 |
transition: background-color .2s ease;
|
|
|
21 |
}
|
22 |
-
.message
|
23 |
background-color: #333232;
|
24 |
}
|
25 |
-
.message
|
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