WinstonDeng commited on
Commit
c8aeeb9
·
verified ·
1 Parent(s): 55e196d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -160
app.py CHANGED
@@ -2,6 +2,7 @@ import streamlit as st
2
  import httpx
3
  import json
4
  import os
 
5
 
6
  # ============================================================
7
  # 配置
@@ -17,94 +18,35 @@ st.set_page_config(
17
  layout="centered",
18
  )
19
 
20
- # 自定义样式
21
  st.markdown("""
22
  <style>
23
- /* 聊天容器 */
24
- .chat-container {
25
- max-width: 800px;
26
- margin: 0 auto;
27
- padding-bottom: 100px;
28
- }
29
-
30
- /* 用户消息 - 右侧 */
31
- .user-message {
32
- display: flex;
33
- justify-content: flex-end;
34
- margin-bottom: 20px;
35
- }
36
- .user-bubble {
37
- background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
38
- color: white;
39
- padding: 12px 16px;
40
- border-radius: 18px 18px 4px 18px;
41
- max-width: 70%;
42
- font-size: 15px;
43
- line-height: 1.5;
44
- word-break: break-word;
45
- }
46
-
47
- /* 助手消息 - 左侧 */
48
- .assistant-message {
49
- display: flex;
50
- flex-direction: column;
51
- align-items: flex-start;
52
- margin-bottom: 20px;
53
- }
54
- .assistant-label {
55
- font-size: 12px;
56
- color: #64748b;
57
- margin-bottom: 6px;
58
- font-weight: 500;
59
- }
60
 
61
- /* 思考气泡 - 可滚动 */
62
- .thinking-bubble {
63
- background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
64
- border: 1px solid #cbd5e1;
65
- border-radius: 12px;
66
- padding: 12px 16px;
67
- margin-bottom: 12px;
68
- max-width: 85%;
69
  max-height: 150px;
70
  overflow-y: auto;
71
  font-size: 13px;
72
- line-height: 1.6;
73
  color: #64748b;
74
- white-space: pre-wrap;
75
- word-break: break-word;
76
  }
77
- .thinking-bubble::-webkit-scrollbar {
78
  width: 4px;
79
  }
80
- .thinking-bubble::-webkit-scrollbar-thumb {
81
- background: #94a3b8;
82
  border-radius: 2px;
83
  }
84
- .thinking-header {
85
- font-size: 11px;
86
  color: #94a3b8;
87
  margin-bottom: 4px;
88
- font-weight: 500;
89
- }
90
-
91
- /* 回答文本 - 直接显示 */
92
- .answer-text {
93
- max-width: 85%;
94
- font-size: 15px;
95
- line-height: 1.7;
96
- color: #1e293b;
97
- white-space: pre-wrap;
98
- word-break: break-word;
99
- }
100
-
101
- /* 隐藏 Streamlit 默认元素 */
102
- #MainMenu {visibility: hidden;}
103
- footer {visibility: hidden;}
104
-
105
- /* 自动滚动脚本的锚点 */
106
- .scroll-anchor {
107
- height: 1px;
108
  }
109
  </style>
110
  """, unsafe_allow_html=True)
@@ -182,57 +124,21 @@ def chat_stream(message: str, history: list, system_prompt: str, max_tokens: int
182
  yield reasoning, f"❌ 错误: {str(e)}"
183
 
184
 
185
- def render_user_message(content: str):
186
- """渲染用户消息(右侧)"""
187
- escaped = content.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
188
- st.markdown(f"""
189
- <div class="user-message">
190
- <div class="user-bubble">{escaped}</div>
191
- </div>
192
- """, unsafe_allow_html=True)
193
-
194
-
195
- def render_assistant_message(thinking: str, answer: str, is_streaming: bool = False):
196
- """渲染助手消息(左侧)"""
197
- thinking_html = ""
198
- if thinking:
199
- # 去掉 <think> 标签
200
- cleaned_thinking = thinking.replace("<think>", "").replace("</think>", "")
201
- escaped_thinking = cleaned_thinking.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
202
- scroll_script = """<script>
203
- (function() {
204
- var el = document.getElementById('thinking-stream');
205
- if (el) el.scrollTop = el.scrollHeight;
206
- })();
207
- </script>""" if is_streaming else ""
208
- thinking_html = f"""
209
- <div class="thinking-header">💭 思考过程</div>
210
- <div class="thinking-bubble" id="thinking-stream">{escaped_thinking}</div>
211
- {scroll_script}
212
- """
213
-
214
- answer_html = ""
215
- if answer:
216
- escaped_answer = answer.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
217
- answer_html = f'<div class="answer-text">{escaped_answer}</div>'
218
-
219
- st.markdown(f"""
220
- <div class="assistant-message">
221
- <div class="assistant-label">🚀 Step-3.5-Flash</div>
222
- {thinking_html}
223
- {answer_html}
224
- </div>
225
- """, unsafe_allow_html=True)
226
-
227
-
228
- def scroll_to_bottom():
229
- """滚动到页面底部"""
230
- st.markdown("""
231
- <script>
232
- window.scrollTo(0, document.body.scrollHeight);
233
- </script>
234
- <div class="scroll-anchor"></div>
235
- """, unsafe_allow_html=True)
236
 
237
 
238
  def main():
@@ -267,9 +173,15 @@ def main():
267
  # 显示历史消息
268
  for msg in st.session_state.messages:
269
  if msg["role"] == "user":
270
- render_user_message(msg["content"])
 
271
  elif msg["role"] == "assistant":
272
- render_assistant_message(msg.get("thinking", ""), msg.get("content", ""))
 
 
 
 
 
273
 
274
  # 示例问题(无消息时显示)
275
  if not st.session_state.messages:
@@ -296,39 +208,45 @@ def main():
296
  if prompt:
297
  # 添加并显示用户消息
298
  st.session_state.messages.append({"role": "user", "content": prompt})
299
- render_user_message(prompt)
300
-
301
- # 创建流式输出占位符
302
- response_placeholder = st.empty()
303
-
304
- full_response = ""
305
- full_thinking = ""
306
-
307
- for thinking, response in chat_stream(
308
- prompt,
309
- st.session_state.messages[:-1],
310
- system_prompt,
311
- max_tokens,
312
- temperature,
313
- top_p,
314
- ):
315
- full_thinking = thinking
316
- full_response = response if response else "▌"
317
-
318
- # 更新显示
319
- with response_placeholder:
320
- render_assistant_message(full_thinking, full_response, is_streaming=True)
321
-
322
- # 滚动到底部
323
- scroll_to_bottom()
324
-
325
- # 保存消息
326
- st.session_state.messages.append({
327
- "role": "assistant",
328
- "content": full_response,
329
- "thinking": full_thinking,
330
- })
331
- st.rerun()
 
 
 
 
 
 
332
 
333
 
334
  if __name__ == "__main__":
 
2
  import httpx
3
  import json
4
  import os
5
+ import re
6
 
7
  # ============================================================
8
  # 配置
 
18
  layout="centered",
19
  )
20
 
21
+ # 简化样式 - 只定义思考区域
22
  st.markdown("""
23
  <style>
24
+ #MainMenu {visibility: hidden;}
25
+ footer {visibility: hidden;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
+ .thinking-container {
28
+ background: #f8fafc;
29
+ border: 1px solid #e2e8f0;
30
+ border-radius: 8px;
31
+ padding: 10px 14px;
32
+ margin-bottom: 10px;
 
 
33
  max-height: 150px;
34
  overflow-y: auto;
35
  font-size: 13px;
36
+ line-height: 1.5;
37
  color: #64748b;
 
 
38
  }
39
+ .thinking-container::-webkit-scrollbar {
40
  width: 4px;
41
  }
42
+ .thinking-container::-webkit-scrollbar-thumb {
43
+ background: #cbd5e1;
44
  border-radius: 2px;
45
  }
46
+ .thinking-label {
47
+ font-size: 12px;
48
  color: #94a3b8;
49
  margin-bottom: 4px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
  </style>
52
  """, unsafe_allow_html=True)
 
124
  yield reasoning, f"❌ 错误: {str(e)}"
125
 
126
 
127
+ def clean_thinking(text: str) -> str:
128
+ """清理思考内容中的标签"""
129
+ if not text:
130
+ return ""
131
+ # 移除 <think> 标签
132
+ text = re.sub(r'</?think>', '', text)
133
+ return text.strip()
134
+
135
+
136
+ def render_thinking_expander(thinking_text: str, is_streaming: bool = False):
137
+ """使用 expander 渲染思考内容"""
138
+ if thinking_text:
139
+ cleaned = clean_thinking(thinking_text)
140
+ with st.expander("💭 思考过程", expanded=is_streaming):
141
+ st.text(cleaned)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
 
144
  def main():
 
173
  # 显示历史消息
174
  for msg in st.session_state.messages:
175
  if msg["role"] == "user":
176
+ with st.chat_message("user"):
177
+ st.markdown(msg["content"])
178
  elif msg["role"] == "assistant":
179
+ with st.chat_message("assistant", avatar="🚀"):
180
+ # 思考内容用 expander
181
+ if msg.get("thinking"):
182
+ render_thinking_expander(msg["thinking"], is_streaming=False)
183
+ # 回答内容用 markdown
184
+ st.markdown(msg.get("content", ""))
185
 
186
  # 示例问题(无消息时显示)
187
  if not st.session_state.messages:
 
208
  if prompt:
209
  # 添加并显示用户消息
210
  st.session_state.messages.append({"role": "user", "content": prompt})
211
+ with st.chat_message("user"):
212
+ st.markdown(prompt)
213
+
214
+ # 助手回复
215
+ with st.chat_message("assistant", avatar="🚀"):
216
+ # 思考内容占位符
217
+ thinking_placeholder = st.empty()
218
+ # 回答内容占位符
219
+ answer_placeholder = st.empty()
220
+
221
+ full_response = ""
222
+ full_thinking = ""
223
+
224
+ for thinking, response in chat_stream(
225
+ prompt,
226
+ st.session_state.messages[:-1],
227
+ system_prompt,
228
+ max_tokens,
229
+ temperature,
230
+ top_p,
231
+ ):
232
+ full_thinking = thinking
233
+ full_response = response if response else "▌"
234
+
235
+ # 更新思考内容
236
+ if full_thinking:
237
+ with thinking_placeholder.container():
238
+ render_thinking_expander(full_thinking, is_streaming=True)
239
+
240
+ # 更新回答内容
241
+ answer_placeholder.markdown(full_response)
242
+
243
+ # 保存消息
244
+ st.session_state.messages.append({
245
+ "role": "assistant",
246
+ "content": full_response,
247
+ "thinking": full_thinking,
248
+ })
249
+ st.rerun()
250
 
251
 
252
  if __name__ == "__main__":