Zenith Wang commited on
Commit
68ecd47
·
1 Parent(s): 284099f

Redesign interface similar to Qwen3-Demo with cleaner layout and better functionality

Browse files
Files changed (1) hide show
  1. app.py +181 -186
app.py CHANGED
@@ -9,11 +9,10 @@ import re
9
 
10
  # 配置
11
  BASE_URL = "https://api.stepfun.com/v1"
12
- # 从环境变量获取API密钥
13
  STEP_API_KEY = os.environ.get("STEP_API_KEY", "")
14
 
15
  def image_to_base64(image):
16
- """将PIL图像转换为base64字符串"""
17
  if image is None:
18
  return None
19
 
@@ -23,275 +22,269 @@ def image_to_base64(image):
23
  img_str = base64.b64encode(buffered.getvalue()).decode('utf-8')
24
  return img_str
25
  elif isinstance(image, str) and os.path.exists(image):
26
- # 如果是文件路径
27
  with open(image, "rb") as image_file:
28
  return base64.b64encode(image_file.read()).decode('utf-8')
29
 
30
  return None
31
 
32
- def extract_cot_and_answer(text):
33
- """从响应中提取CoT推理过程和最终答案"""
34
- # 匹配<reasoning>标签内的内容
35
- reasoning_pattern = re.compile(r'<reasoning>(.*?)</reasoning>', re.DOTALL)
36
 
37
- match = reasoning_pattern.search(text)
38
- if match:
39
- cot = match.group(1).strip()
40
- # 移除reasoning标签及其内容,得到最终答案
41
- answer = reasoning_pattern.sub('', text).strip()
42
- return cot, answer
43
- else:
44
- # 如果没有reasoning标签,整个响应就是答案
45
- return "", text
46
-
47
- def call_step_api_stream(message, history, image=None):
48
- """调用Step API进行流式对话"""
49
- print(f"[DEBUG] Starting API call - Message: {message}, Has Image: {image is not None}")
50
-
51
- if not message and not image:
52
- print("[DEBUG] No message or image provided")
53
- yield history, "", ""
54
  return
55
 
56
  if not STEP_API_KEY:
57
- print("[DEBUG] API key not configured")
58
- error_msg = "❌ API key not configured. Please add STEP_API_KEY in Settings."
59
- history.append([message or "[Image]", error_msg])
60
- yield history, "", ""
61
  return
62
 
63
- print(f"[DEBUG] API Key exists: {bool(STEP_API_KEY)}")
64
-
65
- # 处理消息和图片
66
- display_message = message or ""
67
  image_content = None
 
68
 
69
- if image:
 
 
 
 
 
70
  try:
71
- image_content = image_to_base64(image)
72
- if image_content:
73
- display_message = f"[Image uploaded] {message}" if message else "[Image uploaded]"
74
- print(f"[DEBUG] Image processed successfully")
 
75
  except Exception as e:
76
  print(f"[DEBUG] Failed to process image: {e}")
77
 
78
- # 添加用户消息到历史
79
- history.append([display_message, ""])
80
- yield history, "", ""
81
 
82
- # 构造API消息
83
  messages = []
84
 
85
- # 添加历史对话(只保留文本,不包含标记)
86
- for h in history[:-1]: # 不包含当前消息
87
- if h[0]: # 用户消息
88
- # 移除[Image uploaded]标记
89
- user_text = h[0].replace("[Image uploaded] ", "").replace("[Image uploaded]", "")
 
 
 
 
90
  if user_text:
91
  messages.append({"role": "user", "content": user_text})
92
- if h[1] and not h[1].startswith("❌"): # 助手回复(排除错误消息)
93
- # 提取纯文本内容
94
- assistant_text = h[1]
95
- # 如果包含格式化的CoT和Answer,提取完整内容
96
- if "**Reasoning Process:**" in assistant_text:
97
- # 移除格式化标记,保留原始内容
98
- assistant_text = re.sub(r'\*\*.*?\*\*', '', assistant_text)
99
- assistant_text = assistant_text.replace("💭", "").replace("📝", "").replace("---", "").strip()
100
- messages.append({"role": "assistant", "content": assistant_text})
101
 
102
- # 构造当前消息
103
  if image_content:
104
- # 有图片的情况
105
  current_content = [
106
  {"type": "image_url", "image_url": {"url": f"data:image/jpg;base64,{image_content}", "detail": "high"}}
107
  ]
108
- if message:
109
- current_content.append({"type": "text", "text": message})
110
  messages.append({"role": "user", "content": current_content})
111
  else:
112
- # 纯文本
113
- if message:
114
- messages.append({"role": "user", "content": message})
115
 
116
- print(f"[DEBUG] Messages count: {len(messages)}")
117
 
118
- # 创建客户端
119
  try:
120
  client = OpenAI(api_key=STEP_API_KEY, base_url=BASE_URL)
121
- print("[DEBUG] Client created successfully")
122
- except Exception as e:
123
- print(f"[DEBUG] Client initialization failed: {e}")
124
- history[-1][1] = f"❌ Client initialization failed: {str(e)}"
125
- yield history, "", ""
126
- return
127
-
128
- # 调用API
129
- try:
130
- print("[DEBUG] Calling API...")
131
  response = client.chat.completions.create(
132
  model="step-3",
133
  messages=messages,
134
- temperature=0.7,
135
- max_tokens=2000,
 
136
  stream=True
137
  )
138
- print("[DEBUG] API call successful, processing stream...")
139
 
140
- # 处理流式响应
141
  full_response = ""
142
- current_cot = ""
143
- current_answer = ""
144
- chunk_count = 0
145
-
146
  for chunk in response:
147
- chunk_count += 1
148
  if chunk.choices and len(chunk.choices) > 0:
149
  delta = chunk.choices[0].delta
150
  if hasattr(delta, 'content') and delta.content:
151
  full_response += delta.content
152
-
153
- # 实时提取CoT和答案
154
- current_cot, current_answer = extract_cot_and_answer(full_response)
155
-
156
- # 更新历史中的回复
157
- if current_cot and current_answer:
158
- # 如果有CoT,显示完整格式
159
- history[-1][1] = f"💭 **Reasoning Process:**\n\n{current_cot}\n\n---\n\n📝 **Answer:**\n\n{current_answer}"
160
- elif current_cot:
161
- # 只有CoT,还没有答案
162
- history[-1][1] = f"💭 **Reasoning Process:**\n\n{current_cot}\n\n---\n\n📝 **Answer:**\n\n*Generating...*"
163
- else:
164
- # 没有CoT,直接显示答案
165
- history[-1][1] = current_answer
166
-
167
- if chunk_count % 5 == 0: # 每5个chunk更新一次,减少更新频率
168
- print(f"[DEBUG] Processed {chunk_count} chunks")
169
- yield history, current_cot, current_answer
170
 
171
  if not full_response:
172
- print("[DEBUG] No response content received")
173
  history[-1][1] = "⚠️ No response received from API"
174
- yield history, "", ""
175
- else:
176
- print(f"[DEBUG] Final response length: {len(full_response)} chars")
177
- # 最终更新
178
- yield history, current_cot, current_answer
179
-
180
  except Exception as e:
181
- print(f"[DEBUG] API request failed: {e}")
182
- import traceback
183
- traceback.print_exc()
184
- history[-1][1] = f"❌ API request failed: {str(e)}"
185
- yield history, "", ""
186
-
187
- def clear_all():
188
- """Clear all components"""
189
- return [], None, "", "", ""
190
 
191
  # 创建Gradio界面
192
- with gr.Blocks(title="Step-3", theme=gr.themes.Soft()) as demo:
193
  gr.Markdown("""
194
- # 🤖 Step-3
195
- Hello, I am Step-3!
 
196
  """)
197
 
198
  with gr.Row():
199
- with gr.Column(scale=2):
200
- # 对话界面
201
  chatbot = gr.Chatbot(
202
- height=500,
203
  show_label=False,
204
  elem_id="chatbot",
205
- bubble_full_width=False
 
 
206
  )
207
 
 
208
  with gr.Row():
209
- with gr.Column(scale=6):
210
- # 文本输入框
211
  msg = gr.Textbox(
212
- placeholder="Type your message here...",
213
- show_label=False,
214
  lines=2,
215
- max_lines=4,
216
- container=False,
217
- elem_id="msg"
218
- )
219
- with gr.Column(scale=2):
220
- # 图片上传
221
- image_input = gr.Image(
222
- label="Upload Image",
223
- type="filepath",
224
- height=80,
225
- scale=1
226
  )
227
- with gr.Column(scale=1):
228
- send_btn = gr.Button("Send", variant="primary", scale=1)
229
- clear_btn = gr.Button("Clear", scale=1)
 
 
 
 
 
230
 
231
  with gr.Column(scale=1):
232
- # CoT推理过程展示
233
- gr.Markdown("### 💭 Chain of Thought")
234
- cot_display = gr.Textbox(
235
- label="Reasoning Process",
236
- lines=10,
237
- max_lines=15,
238
- show_label=False,
239
- interactive=False,
240
- show_copy_button=True
241
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
- gr.Markdown("### 📝 Final Answer")
244
- answer_display = gr.Textbox(
245
- label="Answer",
246
- lines=10,
247
- max_lines=15,
248
- show_label=False,
249
- interactive=False,
250
- show_copy_button=True
251
- )
 
 
 
 
252
 
253
  # 事件处理
254
- def on_submit(message, history, image):
255
- if message or image:
256
- return "", history, None
257
- return message, history, image
 
 
 
 
 
 
 
 
 
258
 
259
  # 提交消息
260
  msg.submit(
261
- on_submit,
262
- [msg, chatbot, image_input],
263
- [msg, chatbot, image_input],
264
  queue=False
265
  ).then(
266
- call_step_api_stream,
267
- [msg, chatbot, image_input],
268
- [chatbot, cot_display, answer_display]
269
  )
270
 
271
- send_btn.click(
272
- on_submit,
273
- [msg, chatbot, image_input],
274
- [msg, chatbot, image_input],
275
  queue=False
276
  ).then(
277
- call_step_api_stream,
278
- [msg, chatbot, image_input],
279
- [chatbot, cot_display, answer_display]
280
  )
281
 
 
282
  clear_btn.click(
283
- clear_all,
284
  None,
285
- [chatbot, image_input, msg, cot_display, answer_display]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  )
287
 
288
  # 页脚
289
  gr.Markdown("""
290
  ---
291
- <div style="text-align: center;">
292
- <img src="https://huggingface.co/stepfun-ai/step3/resolve/main/figures/stepfun-logo.png" alt="StepFun Logo" style="height: 40px; margin: 10px;">
293
- <br>
294
- Powered by <a href="https://www.stepfun.com/" target="_blank">StepFun</a>
295
  </div>
296
  """)
297
 
@@ -299,10 +292,12 @@ with gr.Blocks(title="Step-3", theme=gr.themes.Soft()) as demo:
299
  if __name__ == "__main__":
300
  print(f"[DEBUG] Starting app with API key: {'Set' if STEP_API_KEY else 'Not set'}")
301
  print(f"[DEBUG] Base URL: {BASE_URL}")
302
- demo.queue(max_size=10)
 
303
  demo.launch(
304
  server_name="0.0.0.0",
305
  server_port=7860,
306
  share=False,
307
- debug=False
 
308
  )
 
9
 
10
  # 配置
11
  BASE_URL = "https://api.stepfun.com/v1"
 
12
  STEP_API_KEY = os.environ.get("STEP_API_KEY", "")
13
 
14
  def image_to_base64(image):
15
+ """将图像转换为base64字符串"""
16
  if image is None:
17
  return None
18
 
 
22
  img_str = base64.b64encode(buffered.getvalue()).decode('utf-8')
23
  return img_str
24
  elif isinstance(image, str) and os.path.exists(image):
 
25
  with open(image, "rb") as image_file:
26
  return base64.b64encode(image_file.read()).decode('utf-8')
27
 
28
  return None
29
 
30
+ def process_message(message, history, system_prompt, temperature, max_tokens, top_p):
31
+ """处理消息并生成响应"""
32
+ print(f"[DEBUG] Processing message: {message[:100] if message else 'None'}...")
 
33
 
34
+ if not message:
35
+ yield history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  return
37
 
38
  if not STEP_API_KEY:
39
+ history.append([message, "❌ API key not configured. Please add STEP_API_KEY in Settings."])
40
+ yield history
 
 
41
  return
42
 
43
+ # 检查是否有图片(通过检查消息中是否有图片标签)
44
+ image_pattern = r'<img[^>]+src="([^">]+)"'
45
+ image_match = re.search(image_pattern, message)
 
46
  image_content = None
47
+ text_content = message
48
 
49
+ if image_match:
50
+ # 提取图片路径
51
+ image_path = image_match.group(1)
52
+ text_content = re.sub(image_pattern, '', message).strip()
53
+
54
+ # 转换图片为base64
55
  try:
56
+ if image_path.startswith('data:'):
57
+ # 已经是base64格式
58
+ image_content = image_path.split(',')[1]
59
+ else:
60
+ image_content = image_to_base64(image_path)
61
  except Exception as e:
62
  print(f"[DEBUG] Failed to process image: {e}")
63
 
64
+ # 添加到历史
65
+ history.append([message, ""])
66
+ yield history
67
 
68
+ # 构建API消息
69
  messages = []
70
 
71
+ # 添加系统提示词
72
+ if system_prompt:
73
+ messages.append({"role": "system", "content": system_prompt})
74
+
75
+ # 添加历史对话
76
+ for h in history[:-1]:
77
+ if h[0]:
78
+ # 用户消息 - 移除图片标签
79
+ user_text = re.sub(r'<img[^>]+>', '', h[0]).strip()
80
  if user_text:
81
  messages.append({"role": "user", "content": user_text})
82
+ if h[1] and not h[1].startswith("❌"):
83
+ messages.append({"role": "assistant", "content": h[1]})
 
 
 
 
 
 
 
84
 
85
+ # 添加当前消息
86
  if image_content:
 
87
  current_content = [
88
  {"type": "image_url", "image_url": {"url": f"data:image/jpg;base64,{image_content}", "detail": "high"}}
89
  ]
90
+ if text_content:
91
+ current_content.append({"type": "text", "text": text_content})
92
  messages.append({"role": "user", "content": current_content})
93
  else:
94
+ messages.append({"role": "user", "content": text_content})
 
 
95
 
96
+ print(f"[DEBUG] Sending {len(messages)} messages to API")
97
 
98
+ # 创建客户端并调用API
99
  try:
100
  client = OpenAI(api_key=STEP_API_KEY, base_url=BASE_URL)
101
+
 
 
 
 
 
 
 
 
 
102
  response = client.chat.completions.create(
103
  model="step-3",
104
  messages=messages,
105
+ temperature=temperature,
106
+ max_tokens=max_tokens,
107
+ top_p=top_p,
108
  stream=True
109
  )
 
110
 
111
+ # 流式输出
112
  full_response = ""
 
 
 
 
113
  for chunk in response:
 
114
  if chunk.choices and len(chunk.choices) > 0:
115
  delta = chunk.choices[0].delta
116
  if hasattr(delta, 'content') and delta.content:
117
  full_response += delta.content
118
+ history[-1][1] = full_response
119
+ yield history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
  if not full_response:
 
122
  history[-1][1] = "⚠️ No response received from API"
123
+ yield history
124
+
 
 
 
 
125
  except Exception as e:
126
+ print(f"[DEBUG] API error: {e}")
127
+ history[-1][1] = f"❌ Error: {str(e)}"
128
+ yield history
 
 
 
 
 
 
129
 
130
  # 创建Gradio界面
131
+ with gr.Blocks(title="Step-3 Chat", theme=gr.themes.Soft()) as demo:
132
  gr.Markdown("""
133
+ # 🤖 Step-3 Chat
134
+
135
+ Welcome to Step-3, an advanced multimodal AI assistant by StepFun.
136
  """)
137
 
138
  with gr.Row():
139
+ with gr.Column(scale=3):
140
+ # 聊天界面
141
  chatbot = gr.Chatbot(
142
+ height=600,
143
  show_label=False,
144
  elem_id="chatbot",
145
+ bubble_full_width=False,
146
+ avatar_images=None,
147
+ render_markdown=True
148
  )
149
 
150
+ # 输入区域
151
  with gr.Row():
152
+ with gr.Column(scale=10):
 
153
  msg = gr.Textbox(
154
+ label="Message",
155
+ placeholder="Type your message here... (You can drag and drop images)",
156
  lines=2,
157
+ max_lines=10,
158
+ show_label=False,
159
+ elem_id="message-textbox"
 
 
 
 
 
 
 
 
160
  )
161
+ with gr.Column(scale=1, min_width=100):
162
+ submit_btn = gr.Button("Send", variant="primary")
163
+
164
+ # 底部按钮
165
+ with gr.Row():
166
+ clear_btn = gr.Button("🗑️ Clear", scale=1)
167
+ undo_btn = gr.Button("↩️ Undo", scale=1)
168
+ retry_btn = gr.Button("🔄 Retry", scale=1)
169
 
170
  with gr.Column(scale=1):
171
+ # 设置面板
172
+ with gr.Accordion("⚙️ Settings", open=True):
173
+ system_prompt = gr.Textbox(
174
+ label="System Prompt",
175
+ placeholder="You are a helpful assistant...",
176
+ lines=3,
177
+ value="You are Step-3, a helpful AI assistant created by StepFun."
178
+ )
179
+
180
+ temperature = gr.Slider(
181
+ minimum=0,
182
+ maximum=2,
183
+ value=0.7,
184
+ step=0.1,
185
+ label="Temperature"
186
+ )
187
+
188
+ max_tokens = gr.Slider(
189
+ minimum=1,
190
+ maximum=4096,
191
+ value=2048,
192
+ step=1,
193
+ label="Max Tokens"
194
+ )
195
+
196
+ top_p = gr.Slider(
197
+ minimum=0,
198
+ maximum=1,
199
+ value=0.95,
200
+ step=0.01,
201
+ label="Top P"
202
+ )
203
 
204
+ # 示例
205
+ with gr.Accordion("📝 Examples", open=False):
206
+ gr.Examples(
207
+ examples=[
208
+ ["What is machine learning?"],
209
+ ["Write a Python function to calculate fibonacci numbers"],
210
+ ["Explain quantum computing in simple terms"],
211
+ ["What are the benefits of renewable energy?"],
212
+ ["How does blockchain technology work?"]
213
+ ],
214
+ inputs=msg,
215
+ label=""
216
+ )
217
 
218
  # 事件处理
219
+ def user_submit(message, history):
220
+ return "", history
221
+
222
+ def undo_last(history):
223
+ if history:
224
+ return history[:-1]
225
+ return history
226
+
227
+ def retry_last(history):
228
+ if history and history[-1][0]:
229
+ last_message = history[-1][0]
230
+ return history[:-1], last_message
231
+ return history, ""
232
 
233
  # 提交消息
234
  msg.submit(
235
+ user_submit,
236
+ [msg, chatbot],
237
+ [msg, chatbot],
238
  queue=False
239
  ).then(
240
+ process_message,
241
+ [msg, chatbot, system_prompt, temperature, max_tokens, top_p],
242
+ chatbot
243
  )
244
 
245
+ submit_btn.click(
246
+ user_submit,
247
+ [msg, chatbot],
248
+ [msg, chatbot],
249
  queue=False
250
  ).then(
251
+ process_message,
252
+ [msg, chatbot, system_prompt, temperature, max_tokens, top_p],
253
+ chatbot
254
  )
255
 
256
+ # 清空对话
257
  clear_btn.click(
258
+ lambda: ([], ""),
259
  None,
260
+ [chatbot, msg]
261
+ )
262
+
263
+ # 撤销最后一条
264
+ undo_btn.click(
265
+ undo_last,
266
+ chatbot,
267
+ chatbot
268
+ )
269
+
270
+ # 重试最后一条
271
+ retry_btn.click(
272
+ retry_last,
273
+ chatbot,
274
+ [chatbot, msg]
275
+ ).then(
276
+ process_message,
277
+ [msg, chatbot, system_prompt, temperature, max_tokens, top_p],
278
+ chatbot
279
  )
280
 
281
  # 页脚
282
  gr.Markdown("""
283
  ---
284
+ <div style="text-align: center; color: #666;">
285
+ <p>Powered by <a href="https://www.stepfun.com/" target="_blank" style="color: #0969da;">StepFun</a> |
286
+ Model: Step-3 |
287
+ <a href="https://github.com/stepfun-ai" target="_blank" style="color: #0969da;">GitHub</a></p>
288
  </div>
289
  """)
290
 
 
292
  if __name__ == "__main__":
293
  print(f"[DEBUG] Starting app with API key: {'Set' if STEP_API_KEY else 'Not set'}")
294
  print(f"[DEBUG] Base URL: {BASE_URL}")
295
+
296
+ demo.queue(max_size=20)
297
  demo.launch(
298
  server_name="0.0.0.0",
299
  server_port=7860,
300
  share=False,
301
+ debug=False,
302
+ show_error=True
303
  )