XingHe0127 commited on
Commit
307f2ef
·
1 Parent(s): cd3216c

Upload 3 files

Browse files
Files changed (3) hide show
  1. Chatbot.py +146 -0
  2. presets.py +34 -0
  3. utils.py +284 -0
Chatbot.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ # import openai
3
+ import os
4
+ import sys
5
+ from utils import *
6
+ from presets import *
7
+
8
+ my_api_key = os.getenv("OpenAI-API") # 在这里输入你的 API 密钥
9
+ HIDE_MY_KEY = True # 如果你想在UI中隐藏你的 API 密钥,将此值设置为 True
10
+
11
+ gr.Chatbot.postprocess = postprocess
12
+
13
+ #if we are running in Docker
14
+ if os.environ.get('dockerrun') == 'yes':
15
+ dockerflag = True
16
+ else:
17
+ dockerflag = False
18
+
19
+ authflag = False
20
+
21
+ if dockerflag:
22
+ my_api_key = os.environ.get('my_api_key')
23
+ if my_api_key == "empty":
24
+ print("Please give a api key!")
25
+ sys.exit(1)
26
+ #auth
27
+ username = os.environ.get('USERNAME')
28
+ password = os.environ.get('PASSWORD')
29
+ if not (isinstance(username, type(None)) or isinstance(password, type(None))):
30
+ authflag = True
31
+ else:
32
+ if os.path.exists("api_key.txt"):
33
+ with open("api_key.txt", "r") as f:
34
+ my_api_key = f.read().strip()
35
+ if os.path.exists("auth.json"):
36
+ with open("auth.json", "r") as f:
37
+ auth = json.load(f)
38
+ username = auth["username"]
39
+ password = auth["password"]
40
+ if username != "" and password != "":
41
+ authflag = True
42
+
43
+ with gr.Blocks(css=customCSS) as demo:
44
+ gr.HTML(title)
45
+ keyTxt = gr.Textbox(show_label=True, placeholder=f"在这里输入你的OpenAI API-key...",
46
+ value=my_api_key, label="API Key", type="password", visible=not HIDE_MY_KEY).style(container=True)
47
+ chatbot = gr.Chatbot() # .style(color_map=("#1D51EE", "#585A5B"))
48
+ history = gr.State([])
49
+ promptTemplates = gr.State(load_template(get_template_names(plain=True)[0], mode=2))
50
+ TRUECOMSTANT = gr.State(True)
51
+ FALSECONSTANT = gr.State(False)
52
+ topic = gr.State("未命名对话历史记录")
53
+
54
+ with gr.Row():
55
+ with gr.Column(scale=12):
56
+ txt = gr.Textbox(show_label=False, placeholder="在这里输入").style(
57
+ container=False)
58
+ with gr.Column(min_width=50, scale=1):
59
+ submitBtn = gr.Button("🚀", variant="primary")
60
+ with gr.Row():
61
+ emptyBtn = gr.Button("🧹 新的对话")
62
+ retryBtn = gr.Button("🔄 重新生成")
63
+ delLastBtn = gr.Button("🗑️ 删除上条对话")
64
+ reduceTokenBtn = gr.Button("♻️ 总结对话")
65
+ statusDisplay = gr.Markdown("status: ready")
66
+ systemPromptTxt = gr.Textbox(show_label=True, placeholder=f"在这里输入System Prompt...",
67
+ label="System prompt", value=initial_prompt).style(container=True)
68
+ with gr.Accordion(label="加载Prompt模板", open=False):
69
+ with gr.Column():
70
+ with gr.Row():
71
+ with gr.Column(scale=6):
72
+ templateFileSelectDropdown = gr.Dropdown(label="选择Prompt模板集合文件(.csv)", choices=get_template_names(plain=True), multiselect=False)
73
+ with gr.Column(scale=1):
74
+ templateRefreshBtn = gr.Button("🔄 刷新")
75
+ templaeFileReadBtn = gr.Button("📂 读入模板")
76
+ with gr.Row():
77
+ with gr.Column(scale=6):
78
+ templateSelectDropdown = gr.Dropdown(label="从Prompt模板中加载", choices=load_template(get_template_names(plain=True)[0], mode=1), multiselect=False)
79
+ with gr.Column(scale=1):
80
+ templateApplyBtn = gr.Button("⬇️ 应用")
81
+ with gr.Accordion(label="保存/加载对话历史记录(在文本框中输入文件名,点击“保存对话”按钮)", open=False):
82
+ with gr.Column():
83
+ with gr.Row():
84
+ with gr.Column(scale=6):
85
+ saveFileName = gr.Textbox(
86
+ show_label=True, placeholder=f"在这里输入保存的文件名...", label="设置保存文件名", value="对话历史记录").style(container=True)
87
+ with gr.Column(scale=1):
88
+ saveBtn = gr.Button("💾 保存对话")
89
+ with gr.Row():
90
+ with gr.Column(scale=6):
91
+ historyFileSelectDropdown = gr.Dropdown(label="从列表中加载对话", choices=get_history_names(plain=True), multiselect=False)
92
+ with gr.Column(scale=1):
93
+ historyRefreshBtn = gr.Button("🔄 刷新")
94
+ historyReadBtn = gr.Button("📂 读入对话")
95
+ #inputs, top_p, temperature, top_k, repetition_penalty
96
+ with gr.Accordion("参数", open=False):
97
+ top_p = gr.Slider(minimum=-0, maximum=1.0, value=1.0, step=0.05,
98
+ interactive=True, label="Top-p (nucleus sampling)",)
99
+ temperature = gr.Slider(minimum=-0, maximum=5.0, value=1.0,
100
+ step=0.1, interactive=True, label="Temperature",)
101
+ #top_k = gr.Slider( minimum=1, maximum=50, value=4, step=1, interactive=True, label="Top-k",)
102
+ #repetition_penalty = gr.Slider( minimum=0.1, maximum=3.0, value=1.03, step=0.01, interactive=True, label="Repetition Penalty", )
103
+ gr.Markdown(description)
104
+
105
+
106
+ txt.submit(predict, [txt, top_p, temperature, keyTxt,
107
+ chatbot, history, systemPromptTxt], [chatbot, history, statusDisplay])
108
+ txt.submit(reset_textbox, [], [txt])
109
+ submitBtn.click(predict, [txt, top_p, temperature, keyTxt, chatbot,
110
+ history, systemPromptTxt], [chatbot, history, statusDisplay], show_progress=True)
111
+ submitBtn.click(reset_textbox, [], [txt])
112
+ emptyBtn.click(reset_state, outputs=[chatbot, history])
113
+ retryBtn.click(predict, [txt, top_p, temperature, keyTxt, chatbot, history,
114
+ systemPromptTxt, TRUECOMSTANT], [chatbot, history, statusDisplay], show_progress=True)
115
+ delLastBtn.click(delete_last_conversation, [chatbot, history], [
116
+ chatbot, history], show_progress=True)
117
+ reduceTokenBtn.click(predict, [txt, top_p, temperature, keyTxt, chatbot, history,
118
+ systemPromptTxt, FALSECONSTANT, TRUECOMSTANT], [chatbot, history, statusDisplay], show_progress=True)
119
+ saveBtn.click(save_chat_history, [
120
+ saveFileName, systemPromptTxt, history, chatbot], None, show_progress=True)
121
+ saveBtn.click(get_history_names, None, [historyFileSelectDropdown])
122
+ historyRefreshBtn.click(get_history_names, None, [historyFileSelectDropdown])
123
+ historyReadBtn.click(load_chat_history, [historyFileSelectDropdown], [saveFileName, systemPromptTxt, history, chatbot], show_progress=True)
124
+ templateRefreshBtn.click(get_template_names, None, [templateFileSelectDropdown])
125
+ templaeFileReadBtn.click(load_template, [templateFileSelectDropdown], [promptTemplates, templateSelectDropdown], show_progress=True)
126
+ templateApplyBtn.click(lambda x, y: x[y], [promptTemplates, templateSelectDropdown], [systemPromptTxt], show_progress=True)
127
+
128
+ print("启动成功!")
129
+ # 默认开启本地服务器,默认可以直接从IP访问,默认不创建公开分享链接
130
+ demo.title = "Chatbot"
131
+
132
+ #if running in Docker
133
+ if dockerflag:
134
+ if authflag:
135
+ demo.queue().launch(server_name="0.0.0.0", server_port=7860,auth=(username, password))
136
+ else:
137
+ demo.queue().launch(server_name="0.0.0.0", server_port=7860, share=False)
138
+ #if not running in Docker
139
+ else:
140
+ if authflag:
141
+ demo.queue().launch(share=False, auth=(username, password))
142
+ else:
143
+ #demo.queue().launch(share=False) # 改为 share=True 可以创建公开分享链接
144
+ #demo.queue().launch(server_name="0.0.0.0", server_port=7860, share=False) # 可自定义端口
145
+ demo.queue().launch(server_name="0.0.0.0", server_port=7860,auth=(os.environ["username"], os.environ["password"])) # 可设置用户名与密码
146
+ #demo.queue().launch(auth=("在这里填写用户名", "在这里填写密码")) # 适合Nginx反向代理
presets.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ title = """<h1 align="center"></h1>"""
2
+ description = """<div align=center>
3
+
4
+ 此网页基于 `gpt-3.5-turbo` 大型自然语言处理模型
5
+ Build by 星河
6
+
7
+ </div>
8
+ """
9
+ customCSS = """
10
+ code {
11
+ display: inline;
12
+ white-space: break-spaces;
13
+ border-radius: 6px;
14
+ margin: 0 2px 0 2px;
15
+ padding: .2em .4em .1em .4em;
16
+ background-color: rgba(175,184,193,0.2);
17
+ }
18
+ pre {
19
+ display: block;
20
+ white-space: pre;
21
+ background-color: hsla(0, 0%, 0%, 72%);
22
+ border: solid 5px var(--color-border-primary) !important;
23
+ border-radius: 8px;
24
+ padding: 0 1.2rem 1.2rem;
25
+ margin-top: 1em !important;
26
+ color: #FFF;
27
+ box-shadow: inset 0px 8px 16px hsla(0, 0%, 0%, .2)
28
+ }
29
+ pre code, pre code code {
30
+ background-color: transparent !important;
31
+ margin: 0;
32
+ padding: 0;
33
+ }
34
+ """
utils.py ADDED
@@ -0,0 +1,284 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Contains all of the components that can be used with Gradio Interface / Blocks.
2
+ Along with the docs for each component, you can find the names of example demos that use
3
+ each component. These demos are located in the `demo` directory."""
4
+
5
+ from __future__ import annotations
6
+ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Tuple, Type
7
+ import json
8
+ import gradio as gr
9
+ # import openai
10
+ import os
11
+ import traceback
12
+ import requests
13
+ # import markdown
14
+ import csv
15
+ import mdtex2html
16
+
17
+ if TYPE_CHECKING:
18
+ from typing import TypedDict
19
+
20
+ class DataframeData(TypedDict):
21
+ headers: List[str]
22
+ data: List[List[str | int | bool]]
23
+
24
+ initial_prompt = "你是 ChatGPT,由 OpenAI 训练的大型语言模型。请尽可能简洁地回答问题。你的数据库截止日期为:2021年9。"
25
+ API_URL = "https://api.openai.com/v1/chat/completions"
26
+ HISTORY_DIR = "history"
27
+ TEMPLATES_DIR = "templates"
28
+
29
+ def postprocess(
30
+ self, y: List[Tuple[str | None, str | None]]
31
+ ) -> List[Tuple[str | None, str | None]]:
32
+ """
33
+ Parameters:
34
+ y: List of tuples representing the message and response pairs. Each message and response should be a string, which may be in Markdown format.
35
+ Returns:
36
+ List of tuples representing the message and response. Each message and response will be a string of HTML.
37
+ """
38
+ if y is None:
39
+ return []
40
+ for i, (message, response) in enumerate(y):
41
+ y[i] = (
42
+ # None if message is None else markdown.markdown(message),
43
+ # None if response is None else markdown.markdown(response),
44
+ None if message is None else mdtex2html.convert(message),
45
+ None if response is None else mdtex2html.convert(response),
46
+ )
47
+ return y
48
+
49
+ def parse_text(text):
50
+ lines = text.split("\n")
51
+ lines = [line for line in lines if line != ""]
52
+ count = 0
53
+ firstline = False
54
+ for i, line in enumerate(lines):
55
+ if "```" in line:
56
+ count += 1
57
+ items = line.split('`')
58
+ if count % 2 == 1:
59
+ lines[i] = f'<pre><code class="language-{items[-1]}">'
60
+ else:
61
+ lines[i] = f'<br></code></pre>'
62
+ else:
63
+ if i > 0:
64
+ if count % 2 == 1:
65
+ line = line.replace("`", "\`")
66
+ line = line.replace("<", "&lt;")
67
+ line = line.replace(">", "&gt;")
68
+ line = line.replace(" ", "&nbsp;")
69
+ line = line.replace("*", "&ast;")
70
+ line = line.replace("_", "&lowbar;")
71
+ line = line.replace("-", "&#45;")
72
+ line = line.replace(".", "&#46;")
73
+ line = line.replace("!", "&#33;")
74
+ line = line.replace("(", "&#40;")
75
+ line = line.replace(")", "&#41;")
76
+ line = line.replace("$", "&#36;")
77
+ lines[i] = "<br>"+line
78
+ text = "".join(lines)
79
+ return text
80
+
81
+ def predict(inputs, top_p, temperature, openai_api_key, chatbot=[], history=[], system_prompt=initial_prompt, retry=False, summary=False, retry_on_crash = False, stream = True): # repetition_penalty, top_k
82
+
83
+ if retry_on_crash:
84
+ retry = True
85
+
86
+ headers = {
87
+ "Content-Type": "application/json",
88
+ "Authorization": f"Bearer {openai_api_key}"
89
+ }
90
+
91
+ chat_counter = len(history) // 2
92
+
93
+ print(f"chat_counter - {chat_counter}")
94
+
95
+ messages = []
96
+ if chat_counter:
97
+ for index in range(0, 2*chat_counter, 2):
98
+ temp1 = {}
99
+ temp1["role"] = "user"
100
+ temp1["content"] = history[index]
101
+ temp2 = {}
102
+ temp2["role"] = "assistant"
103
+ temp2["content"] = history[index+1]
104
+ if temp1["content"] != "":
105
+ if temp2["content"] != "" or retry:
106
+ messages.append(temp1)
107
+ messages.append(temp2)
108
+ else:
109
+ messages[-1]['content'] = temp2['content']
110
+ if retry and chat_counter:
111
+ if retry_on_crash:
112
+ messages = messages[-6:]
113
+ messages.pop()
114
+ elif summary:
115
+ history = [*[i["content"] for i in messages[-2:]], "我们刚刚聊了什么?"]
116
+ messages.append(compose_user(
117
+ "请帮我总结一下上述对话的内容,实现减少字数的同时,保证对话的质量。在总结中不要加入这一句话。"))
118
+ else:
119
+ temp3 = {}
120
+ temp3["role"] = "user"
121
+ temp3["content"] = inputs
122
+ messages.append(temp3)
123
+ chat_counter += 1
124
+ messages = [compose_system(system_prompt), *messages]
125
+ # messages
126
+ payload = {
127
+ "model": "gpt-3.5-turbo",
128
+ "messages": messages, # [{"role": "user", "content": f"{inputs}"}],
129
+ "temperature": temperature, # 1.0,
130
+ "top_p": top_p, # 1.0,
131
+ "n": 1,
132
+ "stream": stream,
133
+ "presence_penalty": 0,
134
+ "frequency_penalty": 0,
135
+ }
136
+
137
+ if not summary:
138
+ history.append(inputs)
139
+ else:
140
+ print("精简中...")
141
+
142
+ print(f"payload: {payload}")
143
+ # make a POST request to the API endpoint using the requests.post method, passing in stream=True
144
+ try:
145
+ response = requests.post(API_URL, headers=headers, json=payload, stream=True)
146
+ except:
147
+ history.append("")
148
+ chatbot.append(inputs, "")
149
+ yield history, chatbot, f"出现了网络错误"
150
+ return
151
+
152
+ token_counter = 0
153
+ partial_words = ""
154
+
155
+ counter = 0
156
+ if stream:
157
+ chatbot.append((parse_text(history[-1]), ""))
158
+ for chunk in response.iter_lines():
159
+ if counter == 0:
160
+ counter += 1
161
+ continue
162
+ counter += 1
163
+ # check whether each line is non-empty
164
+ if chunk:
165
+ # decode each line as response data is in bytes
166
+ try:
167
+ if len(json.loads(chunk.decode()[6:])['choices'][0]["delta"]) == 0:
168
+ chunkjson = json.loads(chunk.decode()[6:])
169
+ status_text = f"id: {chunkjson['id']}, finish_reason: {chunkjson['choices'][0]['finish_reason']}"
170
+ yield chatbot, history, status_text
171
+ break
172
+ except Exception as e:
173
+ traceback.print_exc()
174
+ if not retry_on_crash:
175
+ print("正在尝试使用缩短的context重新生成……")
176
+ chatbot.pop()
177
+ history.append("")
178
+ yield next(predict(inputs, top_p, temperature, openai_api_key, chatbot, history, system_prompt, retry, summary=False, retry_on_crash=True, stream=False))
179
+ else:
180
+ msg = "☹️发生了错误:生成失败,请检查网络"
181
+ print(msg)
182
+ history.append(inputs, "")
183
+ chatbot.append(inputs, msg)
184
+ yield chatbot, history, "status: ERROR"
185
+ break
186
+ chunkjson = json.loads(chunk.decode()[6:])
187
+ status_text = f"id: {chunkjson['id']}, finish_reason: {chunkjson['choices'][0]['finish_reason']}"
188
+ partial_words = partial_words + \
189
+ json.loads(chunk.decode()[6:])[
190
+ 'choices'][0]["delta"]["content"]
191
+ if token_counter == 0:
192
+ history.append(" " + partial_words)
193
+ else:
194
+ history[-1] = partial_words
195
+ chatbot[-1] = (parse_text(history[-2]), parse_text(history[-1]))
196
+ token_counter += 1
197
+ yield chatbot, history, status_text
198
+ else:
199
+ try:
200
+ responsejson = json.loads(response.text)
201
+ content = responsejson["choices"][0]["message"]["content"]
202
+ history.append(content)
203
+ chatbot.append((parse_text(history[-2]), parse_text(content)))
204
+ status_text = "精简完成"
205
+ except:
206
+ chatbot.append((parse_text(history[-1]), "☹️发生了错误,请检查网络连接或者稍后再试。"))
207
+ status_text = "status: ERROR"
208
+ yield chatbot, history, status_text
209
+
210
+
211
+
212
+ def delete_last_conversation(chatbot, history):
213
+ if "☹️发生了错误" in chatbot[-1][1]:
214
+ chatbot.pop()
215
+ print(history)
216
+ return chatbot, history
217
+ history.pop()
218
+ history.pop()
219
+ print(history)
220
+ return chatbot, history
221
+
222
+ def save_chat_history(filename, system, history, chatbot):
223
+ if filename == "":
224
+ return
225
+ if not filename.endswith(".json"):
226
+ filename += ".json"
227
+ os.makedirs(HISTORY_DIR, exist_ok=True)
228
+ json_s = {"system": system, "history": history, "chatbot": chatbot}
229
+ print(json_s)
230
+ with open(os.path.join(HISTORY_DIR, filename), "w") as f:
231
+ json.dump(json_s, f)
232
+
233
+
234
+ def load_chat_history(filename):
235
+ with open(os.path.join(HISTORY_DIR, filename), "r") as f:
236
+ json_s = json.load(f)
237
+ print(json_s)
238
+ return filename, json_s["system"], json_s["history"], json_s["chatbot"]
239
+
240
+
241
+ def get_file_names(dir, plain=False, filetype=".json"):
242
+ # find all json files in the current directory and return their names
243
+ try:
244
+ files = sorted([f for f in os.listdir(dir) if f.endswith(filetype)])
245
+ except FileNotFoundError:
246
+ files = []
247
+ if plain:
248
+ return files
249
+ else:
250
+ return gr.Dropdown.update(choices=files)
251
+
252
+ def get_history_names(plain=False):
253
+ return get_file_names(HISTORY_DIR, plain)
254
+
255
+ def load_template(filename, mode=0):
256
+ lines = []
257
+ with open(os.path.join(TEMPLATES_DIR, filename), "r", encoding="utf8") as csvfile:
258
+ reader = csv.reader(csvfile)
259
+ lines = list(reader)
260
+ lines = lines[1:]
261
+ if mode == 1:
262
+ return sorted([row[0] for row in lines])
263
+ elif mode == 2:
264
+ return {row[0]:row[1] for row in lines}
265
+ else:
266
+ return {row[0]:row[1] for row in lines}, gr.Dropdown.update(choices=sorted([row[0] for row in lines]))
267
+
268
+ def get_template_names(plain=False):
269
+ return get_file_names(TEMPLATES_DIR, plain, filetype=".csv")
270
+
271
+ def reset_state():
272
+ return [], []
273
+
274
+
275
+ def compose_system(system_prompt):
276
+ return {"role": "system", "content": system_prompt}
277
+
278
+
279
+ def compose_user(user_input):
280
+ return {"role": "user", "content": user_input}
281
+
282
+
283
+ def reset_textbox():
284
+ return gr.update(value='')