jingyaogong commited on
Commit
261efa7
·
1 Parent(s): 814ce56

update space

Browse files
Files changed (1) hide show
  1. app.py +288 -0
app.py ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import re
3
+ import time
4
+
5
+ import numpy as np
6
+ import streamlit as st
7
+ import torch
8
+
9
+ st.set_page_config(page_title="MiniMind", initial_sidebar_state="collapsed")
10
+
11
+ # 在文件开头的 CSS 样式中修改按钮样式
12
+ st.markdown("""
13
+ <style>
14
+ /* 添加操作按钮样式 */
15
+ .stButton button {
16
+ border-radius: 50% !important; /* 改为圆形 */
17
+ width: 32px !important; /* 固定宽度 */
18
+ height: 32px !important; /* 固定高度 */
19
+ padding: 0 !important; /* 移除内边距 */
20
+ background-color: transparent !important;
21
+ border: 1px solid #ddd !important;
22
+ display: flex !important;
23
+ align-items: center !important;
24
+ justify-content: center !important;
25
+ font-size: 14px !important;
26
+ color: #666 !important; /* 更柔和的颜色 */
27
+ margin: 5px 10px 5px 0 !important; /* 调整按钮间距 */
28
+ }
29
+ .stButton button:hover {
30
+ border-color: #999 !important;
31
+ color: #333 !important;
32
+ background-color: #f5f5f5 !important;
33
+ }
34
+ .stMainBlockContainer > div:first-child {
35
+ margin-top: -50px !important;
36
+ }
37
+ .stApp > div:last-child {
38
+ margin-bottom: -35px !important;
39
+ }
40
+
41
+ /* 重置按钮基础样式 */
42
+ .stButton > button {
43
+ all: unset !important; /* 重置所有默认样式 */
44
+ box-sizing: border-box !important;
45
+ border-radius: 50% !important;
46
+ width: 18px !important;
47
+ height: 18px !important;
48
+ min-width: 18px !important;
49
+ min-height: 18px !important;
50
+ max-width: 18px !important;
51
+ max-height: 18px !important;
52
+ padding: 0 !important;
53
+ background-color: transparent !important;
54
+ border: 1px solid #ddd !important;
55
+ display: flex !important;
56
+ align-items: center !important;
57
+ justify-content: center !important;
58
+ font-size: 14px !important;
59
+ color: #888 !important;
60
+ cursor: pointer !important;
61
+ transition: all 0.2s ease !important;
62
+ margin: 0 2px !important; /* 调整这里的 margin 值 */
63
+ }
64
+
65
+ </style>
66
+ """, unsafe_allow_html=True)
67
+
68
+ system_prompt = []
69
+ device = "cuda" if torch.cuda.is_available() else "cpu"
70
+
71
+
72
+ def process_assistant_content(content):
73
+ if 'R1' not in MODEL_PATHS[selected_model][1]:
74
+ return content
75
+
76
+ if '<think>' in content and '</think>' in content:
77
+ content = re.sub(r'(<think>)(.*?)(</think>)',
78
+ r'<details style="font-style: italic; background: rgba(222, 222, 222, 0.5); padding: 10px; border-radius: 10px;"><summary style="font-weight:bold;">推理内容(展开)</summary>\2</details>',
79
+ content,
80
+ flags=re.DOTALL)
81
+
82
+ if '<think>' in content and '</think>' not in content:
83
+ content = re.sub(r'<think>(.*?)$',
84
+ r'<details open style="font-style: italic; background: rgba(222, 222, 222, 0.5); padding: 10px; border-radius: 10px;"><summary style="font-weight:bold;">推理中...</summary>\1</details>',
85
+ content,
86
+ flags=re.DOTALL)
87
+
88
+ if '<think>' not in content and '</think>' in content:
89
+ content = re.sub(r'(.*?)</think>',
90
+ r'<details style="font-style: italic; background: rgba(222, 222, 222, 0.5); padding: 10px; border-radius: 10px;"><summary style="font-weight:bold;">推理内容(展开)</summary>\1</details>',
91
+ content,
92
+ flags=re.DOTALL)
93
+
94
+ return content
95
+
96
+
97
+ @st.cache_resource
98
+ def load_model_tokenizer(model_path):
99
+ model = AutoModelForCausalLM.from_pretrained(
100
+ model_path,
101
+ trust_remote_code=True
102
+ )
103
+ tokenizer = AutoTokenizer.from_pretrained(
104
+ model_path,
105
+ use_fast=False,
106
+ trust_remote_code=True
107
+ )
108
+ model = model.eval().to(device)
109
+ return model, tokenizer
110
+
111
+
112
+ def clear_chat_messages():
113
+ del st.session_state.messages
114
+ del st.session_state.chat_messages
115
+
116
+
117
+ def init_chat_messages():
118
+ if "messages" in st.session_state:
119
+ for i, message in enumerate(st.session_state.messages):
120
+ if message["role"] == "assistant":
121
+ with st.chat_message("assistant", avatar=image_url):
122
+ st.markdown(process_assistant_content(message["content"]), unsafe_allow_html=True)
123
+ # 在消息内容下方添加按钮
124
+ if st.button("🗑", key=f"delete_{i}"):
125
+ st.session_state.messages.pop(i)
126
+ st.session_state.messages.pop(i - 1)
127
+ st.session_state.chat_messages.pop(i)
128
+ st.session_state.chat_messages.pop(i - 1)
129
+ st.rerun()
130
+ else:
131
+ st.markdown(
132
+ f'<div style="display: flex; justify-content: flex-end;"><div style="display: inline-block; margin: 10px 0; padding: 8px 12px 8px 12px; background-color: #ddd; border-radius: 10px; color: black;">{message["content"]}</div></div>',
133
+ unsafe_allow_html=True)
134
+
135
+ else:
136
+ st.session_state.messages = []
137
+ st.session_state.chat_messages = []
138
+
139
+ return st.session_state.messages
140
+
141
+
142
+ # 添加这两个辅助函数
143
+ def regenerate_answer(index):
144
+ st.session_state.messages.pop()
145
+ st.session_state.chat_messages.pop()
146
+ st.rerun()
147
+
148
+
149
+ def delete_conversation(index):
150
+ st.session_state.messages.pop(index)
151
+ st.session_state.messages.pop(index - 1)
152
+ st.session_state.chat_messages.pop(index)
153
+ st.session_state.chat_messages.pop(index - 1)
154
+ st.rerun()
155
+
156
+
157
+ # 侧边栏模型选择
158
+ st.sidebar.title("模型设定调整")
159
+
160
+ st.sidebar.text("【注】训练数据偏差,增加上下文记忆时\n多轮对话(较单轮)容易出现能力衰减")
161
+ st.session_state.history_chat_num = st.sidebar.slider("Number of Historical Dialogues", 0, 6, 0, step=2)
162
+ # st.session_state.history_chat_num = 0
163
+ st.session_state.max_new_tokens = st.sidebar.slider("Max Sequence Length", 256, 8192, 8192, step=1)
164
+ st.session_state.top_p = st.sidebar.slider("Top-P", 0.8, 0.99, 0.85, step=0.01)
165
+ st.session_state.temperature = st.sidebar.slider("Temperature", 0.6, 1.2, 0.85, step=0.01)
166
+
167
+ # 模型路径映射
168
+ MODEL_PATHS = {
169
+ "MiniMind2-R1 (0.1B)": ["./MiniMind2-R1", "MiniMind2-R1"],
170
+ "MiniMind2 (0.1B)": ["./MiniMind2", "MiniMind2"],
171
+ }
172
+
173
+ selected_model = st.sidebar.selectbox('Models', list(MODEL_PATHS.keys()), index=0) # 默认选择 MiniMind2
174
+ model_path = MODEL_PATHS[selected_model][0]
175
+
176
+ slogan = f"Hi, I'm {MODEL_PATHS[selected_model][1]}"
177
+
178
+ image_url = "https://www.modelscope.cn/api/v1/studio/gongjy/MiniMind/repo?Revision=master&FilePath=images%2Flogo2.png&View=true"
179
+
180
+ st.markdown(
181
+ f'<div style="display: flex; flex-direction: column; align-items: center; text-align: center; margin: 0; padding: 0;">'
182
+ '<div style="font-style: italic; font-weight: 900; margin: 0; padding-top: 4px; display: flex; align-items: center; justify-content: center; flex-wrap: wrap; width: 100%;">'
183
+ f'<img src="{image_url}" style="width: 45px; height: 45px; "> '
184
+ f'<span style="font-size: 26px; margin-left: 10px;">{slogan}</span>'
185
+ '</div>'
186
+ '<span style="color: #bbb; font-style: italic; margin-top: 6px; margin-bottom: 10px;">内容完全由AI生成,请务必仔细甄别<br>Content AI-generated, please discern with care</span>'
187
+ '</div>',
188
+ unsafe_allow_html=True
189
+ )
190
+
191
+
192
+ def setup_seed(seed):
193
+ random.seed(seed)
194
+ np.random.seed(seed)
195
+ torch.manual_seed(seed)
196
+ torch.cuda.manual_seed(seed)
197
+ torch.cuda.manual_seed_all(seed)
198
+ torch.backends.cudnn.deterministic = True
199
+ torch.backends.cudnn.benchmark = False
200
+
201
+
202
+ def main():
203
+ model, tokenizer = load_model_tokenizer(model_path)
204
+
205
+ # 初始化消息列表
206
+ if "messages" not in st.session_state:
207
+ st.session_state.messages = []
208
+ st.session_state.chat_messages = []
209
+
210
+ # Use session state messages
211
+ messages = st.session_state.messages
212
+
213
+ # 在显示历史消息的循环中
214
+ for i, message in enumerate(messages):
215
+ if message["role"] == "assistant":
216
+ with st.chat_message("assistant", avatar=image_url):
217
+ st.markdown(process_assistant_content(message["content"]), unsafe_allow_html=True)
218
+ if st.button("×", key=f"delete_{i}"):
219
+ # 删除当前消息及其之后的所有消息
220
+ st.session_state.messages = st.session_state.messages[:i - 1]
221
+ st.session_state.chat_messages = st.session_state.chat_messages[:i - 1]
222
+ st.rerun()
223
+ else:
224
+ st.markdown(
225
+ f'<div style="display: flex; justify-content: flex-end;"><div style="display: inline-block; margin: 10px 0; padding: 8px 12px 8px 12px; background-color: gray; border-radius: 10px; color:white; ">{message["content"]}</div></div>',
226
+ unsafe_allow_html=True)
227
+
228
+ # 处理新的输入或重新生成
229
+ prompt = st.chat_input(key="input", placeholder="给 MiniMind 发送消息")
230
+
231
+ # 检查是否需要重新生成
232
+ if hasattr(st.session_state, 'regenerate') and st.session_state.regenerate:
233
+ prompt = st.session_state.last_user_message
234
+ regenerate_index = st.session_state.regenerate_index # 获取重新生成的位置
235
+ # 清除所有重新生成相关的状态
236
+ delattr(st.session_state, 'regenerate')
237
+ delattr(st.session_state, 'last_user_message')
238
+ delattr(st.session_state, 'regenerate_index')
239
+
240
+ if prompt:
241
+ st.markdown(
242
+ f'<div style="display: flex; justify-content: flex-end;"><div style="display: inline-block; margin: 10px 0; padding: 8px 12px 8px 12px; background-color: gray; border-radius: 10px; color:white; ">{prompt}</div></div>',
243
+ unsafe_allow_html=True)
244
+ messages.append({"role": "user", "content": prompt})
245
+ st.session_state.chat_messages.append({"role": "user", "content": prompt})
246
+
247
+ with st.chat_message("assistant", avatar=image_url):
248
+ placeholder = st.empty()
249
+ random_seed = random.randint(0, 2 ** 32 - 1)
250
+ setup_seed(random_seed)
251
+
252
+ st.session_state.chat_messages = system_prompt + st.session_state.chat_messages[
253
+ -(st.session_state.history_chat_num + 1):]
254
+ new_prompt = tokenizer.apply_chat_template(
255
+ st.session_state.chat_messages,
256
+ tokenize=False,
257
+ add_generation_prompt=True
258
+ )[-(st.session_state.max_new_tokens - 1):]
259
+
260
+ x = torch.tensor(tokenizer(new_prompt)['input_ids'], device=device).unsqueeze(0)
261
+ with torch.no_grad():
262
+ res_y = model.generate(x, tokenizer.eos_token_id, max_new_tokens=st.session_state.max_new_tokens,
263
+ temperature=st.session_state.temperature,
264
+ top_p=st.session_state.top_p, stream=True)
265
+ try:
266
+ for y in res_y:
267
+ answer = tokenizer.decode(y[0].tolist(), skip_special_tokens=True)
268
+ if (answer and answer[-1] == '�') or not answer:
269
+ continue
270
+ placeholder.markdown(process_assistant_content(answer), unsafe_allow_html=True)
271
+ except StopIteration:
272
+ print("No answer")
273
+
274
+ assistant_answer = answer.replace(new_prompt, "")
275
+ messages.append({"role": "assistant", "content": assistant_answer})
276
+ st.session_state.chat_messages.append({"role": "assistant", "content": assistant_answer})
277
+
278
+ with st.empty():
279
+ if st.button("×", key=f"delete_{len(messages) - 1}"):
280
+ st.session_state.messages = st.session_state.messages[:-2]
281
+ st.session_state.chat_messages = st.session_state.chat_messages[:-2]
282
+ st.rerun()
283
+
284
+
285
+ if __name__ == "__main__":
286
+ from transformers import AutoModelForCausalLM, AutoTokenizer
287
+
288
+ main()