Zenith Wang commited on
Commit
4f9934c
·
1 Parent(s): a493d0c

Use Box wrapper and ID selector for more precise CSS targeting

Browse files
Files changed (1) hide show
  1. app.py +262 -276
app.py CHANGED
@@ -1,132 +1,165 @@
1
  import gradio as gr
2
- import time
3
  import base64
4
  from openai import OpenAI
5
- import os
6
- from io import BytesIO
7
  from PIL import Image
8
- import re
 
 
 
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
-
19
- if isinstance(image, Image.Image):
20
- buffered = BytesIO()
21
- image.save(buffered, format="PNG")
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, images, system_prompt, temperature, max_tokens, top_p):
31
- """处理消息并生成响应,支持可选的多图片输入"""
32
- print(f"[DEBUG] Processing message: {message[:100] if message else 'None'}...")
33
- print(f"[DEBUG] Has images: {images is not None and len(images) > 0 if images else False}")
34
-
35
  if not message and not images:
36
- print("[DEBUG] No message or images provided, skipping")
37
- yield history
38
- return
39
 
40
- if not STEP_API_KEY:
41
- print("[DEBUG] No API key configured")
42
- error_msg = "❌ API key not configured. Please add STEP_API_KEY in Settings."
43
- if images and message:
44
- display_msg = f"[{len(images)} Images] {message}"
45
- elif images:
46
- display_msg = f"[{len(images)} Images]"
47
  else:
48
- display_msg = message
49
- history.append([display_msg, error_msg])
50
- yield history
51
- return
52
 
53
- # 处理多张图片
54
- image_contents = []
55
- if images:
56
- for img_path in images:
57
- try:
58
- # 获取文件路径
59
- if hasattr(img_path, 'name'):
60
- img_file = img_path.name
61
- else:
62
- img_file = img_path
63
-
64
- # 转换图片为base64
65
- with Image.open(img_file) as img:
66
- image_content = image_to_base64(img)
67
- if image_content:
68
- image_contents.append(image_content)
69
- print(f"[DEBUG] Image {len(image_contents)} processed successfully")
70
- except Exception as e:
71
- print(f"[DEBUG] Failed to process image: {e}")
72
- history.append([message or f"[{len(images)} Images]", f"❌ Failed to process image: {str(e)}"])
73
- yield history
74
- return
75
 
76
- # 构造显示消息
77
- text_content = message or ""
78
- if image_contents and message:
79
- display_message = f"🖼️ [{len(image_contents)} Image{'s' if len(image_contents) > 1 else ''}] {message}"
80
- elif image_contents:
81
- display_message = f"🖼️ [{len(image_contents)} Image{'s' if len(image_contents) > 1 else ''}]"
 
 
 
 
 
 
 
 
 
 
 
82
  else:
83
- display_message = message
84
-
85
- # 添加到历史
86
- history.append([display_message, ""])
87
- yield history
88
-
89
- # 构建API消息
90
- messages = []
91
 
92
- # 添加系统提示词
93
- if system_prompt:
94
- messages.append({"role": "system", "content": system_prompt})
 
 
95
 
96
- # 添加历史对话
97
- for h in history[:-1]:
98
- if h[0]:
99
- # 用户消息 - 移除图片标签
100
- user_text = re.sub(r'<img[^>]+>', '', h[0]).strip()
101
- if user_text:
102
- messages.append({"role": "user", "content": user_text})
103
- if h[1] and not h[1].startswith("❌"):
104
- messages.append({"role": "assistant", "content": h[1]})
105
 
106
- # 添加当前消息
107
- if image_contents:
108
- current_content = []
109
- # 添加所有图片
110
- for img_base64 in image_contents:
111
- current_content.append({
112
- "type": "image_url",
113
- "image_url": {"url": f"data:image/jpg;base64,{img_base64}", "detail": "high"}
114
- })
115
- # 添加文本
116
- if text_content:
117
- current_content.append({"type": "text", "text": text_content})
118
- messages.append({"role": "user", "content": current_content})
119
- else:
120
- messages.append({"role": "user", "content": text_content})
121
 
122
- print(f"[DEBUG] Sending {len(messages)} messages to API")
123
- print(f"[DEBUG] Last message: {messages[-1]}")
 
124
 
125
- # 创建客户端并调用API
126
  try:
127
- # 清除所有可能的代理环境变量
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  import os
129
- proxy_vars = ['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy',
 
 
 
130
  'ALL_PROXY', 'all_proxy', 'NO_PROXY', 'no_proxy']
131
  for var in proxy_vars:
132
  if var in os.environ:
@@ -136,16 +169,18 @@ def process_message(message, history, images, system_prompt, temperature, max_to
136
  # 尝试创建客户端
137
  try:
138
  # 方法1:直接创建
139
- client = OpenAI(api_key=STEP_API_KEY, base_url=BASE_URL)
 
 
 
140
  print("[DEBUG] Client created successfully (method 1)")
141
  except TypeError as e:
142
  if 'proxies' in str(e):
143
  print(f"[DEBUG] Method 1 failed with proxy error, trying method 2")
144
- # 方法2:使用httpx客户端
145
- import httpx
146
  http_client = httpx.Client(trust_env=False)
147
  client = OpenAI(
148
- api_key=STEP_API_KEY,
149
  base_url=BASE_URL,
150
  http_client=http_client
151
  )
@@ -153,7 +188,9 @@ def process_message(message, history, images, system_prompt, temperature, max_to
153
  else:
154
  raise e
155
 
156
- print("[DEBUG] Calling API...")
 
 
157
  response = client.chat.completions.create(
158
  model="step-3",
159
  messages=messages,
@@ -163,122 +200,127 @@ def process_message(message, history, images, system_prompt, temperature, max_to
163
  stream=True
164
  )
165
 
166
- print("[DEBUG] API call successful, processing stream...")
167
 
168
  # 流式输出
169
  full_response = ""
170
- chunk_count = 0
171
  in_reasoning = False
172
  reasoning_content = ""
173
  final_content = ""
174
 
175
  for chunk in response:
176
- chunk_count += 1
177
- if chunk.choices and len(chunk.choices) > 0:
178
- delta = chunk.choices[0].delta
179
- if hasattr(delta, 'content') and delta.content:
180
- content = delta.content
181
- full_response += content
182
 
183
- # 检测 <reasoning> 标签
184
- if '<reasoning>' in content:
185
  in_reasoning = True
186
- # 分割内容
187
- parts = content.split('<reasoning>')
188
- final_content += parts[0]
189
  if len(parts) > 1:
190
- reasoning_content += parts[1]
191
- elif '</reasoning>' in content:
192
- # 结束推理部分
193
- parts = content.split('</reasoning>')
194
- if parts[0]:
195
- reasoning_content += parts[0]
196
  in_reasoning = False
 
197
  if len(parts) > 1:
198
- final_content += parts[1]
 
199
  elif in_reasoning:
200
- # 在推理标签内
201
- reasoning_content += content
 
 
 
202
  else:
203
- # 在推理标签外
204
- final_content += content
 
205
 
206
- # 实时更新显示
207
  if reasoning_content and final_content:
208
- # 有推理和最终答案
209
- display_text = f"💭 **Chain of Thought:**\n\n{reasoning_content}\n\n---\n\n📝 **Answer:**\n\n{final_content}"
210
  elif reasoning_content:
211
- # 只有推理过程
212
- display_text = f"💭 **Chain of Thought:**\n\n{reasoning_content}\n\n---\n\n📝 **Answer:**\n\n*Generating...*"
213
  else:
214
- # 只有答案或普通回复
215
  display_text = full_response
216
 
217
  history[-1][1] = display_text
218
-
219
- if chunk_count % 5 == 0:
220
- print(f"[DEBUG] Received {chunk_count} chunks, {len(full_response)} chars")
221
  yield history
222
 
223
- print(f"[DEBUG] Stream complete. Total chunks: {chunk_count}, Total chars: {len(full_response)}")
224
-
225
  # 最终格式化
226
- if reasoning_content:
227
- # 如果有推理内容,使用格式化显示
228
- final_display = f"💭 **Chain of Thought:**\n\n{reasoning_content}\n\n---\n\n📝 **Answer:**\n\n{final_content.strip()}"
229
  history[-1][1] = final_display
230
- yield history
 
 
 
 
231
 
232
- if not full_response:
233
- print("[DEBUG] No response content received")
234
- history[-1][1] = "⚠️ No response received from API"
235
- yield history
236
-
237
  except Exception as e:
238
- print(f"[DEBUG] API error: {e}")
239
- import traceback
240
  traceback.print_exc()
241
  history[-1][1] = f"❌ Error: {str(e)}"
242
  yield history
243
 
244
  # 创建Gradio界面
245
  css = """
246
- /* 让文件上传框与文本框高度一致 */
247
- .compact-file {
248
  height: 52px !important;
 
 
 
 
249
  }
250
- .compact-file > div:first-child {
 
 
251
  height: 52px !important;
252
- display: flex !important;
253
- flex-direction: column !important;
254
  }
255
- .compact-file label {
256
- display: none !important; /* 隐藏标签以节省空间 */
 
 
257
  }
258
- .compact-file .wrap {
 
259
  height: 52px !important;
 
260
  padding: 0 !important;
261
  }
262
- .compact-file .file-preview {
 
 
263
  max-height: 52px !important;
264
- overflow-y: auto !important;
265
- font-size: 12px !important;
266
- padding: 8px !important;
267
  }
268
- .compact-file input[type="file"] {
269
- height: 52px !important;
 
 
 
 
 
 
 
 
 
270
  font-size: 12px !important;
 
271
  }
272
- .compact-file .upload-container {
273
- height: 52px !important;
 
 
274
  }
275
- /* 确保上传按钮区域也是正确高度 */
276
- .compact-file .upload-area {
277
- height: 52px !important;
278
  min-height: 52px !important;
279
- display: flex !important;
280
- align-items: center !important;
281
- justify-content: center !important;
282
  }
283
  """
284
 
@@ -289,9 +331,12 @@ with gr.Blocks(title="Step-3", theme=gr.themes.Soft(), css=css) as demo:
289
  Welcome to Step-3, an advanced multimodal AI assistant by <a href="https://stepfun.com/" target="_blank" style="color: #0969da;">StepFun</a>.
290
  """)
291
 
 
 
 
 
292
  with gr.Row():
293
  with gr.Column(scale=3):
294
- # 聊天界面
295
  chatbot = gr.Chatbot(
296
  height=600,
297
  show_label=False,
@@ -313,14 +358,16 @@ with gr.Blocks(title="Step-3", theme=gr.themes.Soft(), css=css) as demo:
313
  elem_id="message-textbox"
314
  )
315
  with gr.Column(scale=2):
316
- image_input = gr.File(
317
- label="Images",
318
- file_count="multiple",
319
- file_types=[".png", ".jpg", ".jpeg", ".gif", ".webp"],
320
- interactive=True,
321
- show_label=False,
322
- elem_classes="compact-file"
323
- )
 
 
324
  with gr.Column(scale=1, min_width=100):
325
  submit_btn = gr.Button("Send", variant="primary")
326
 
@@ -332,90 +379,46 @@ with gr.Blocks(title="Step-3", theme=gr.themes.Soft(), css=css) as demo:
332
 
333
  with gr.Column(scale=1):
334
  # 设置面板
335
- with gr.Accordion("⚙️ Settings", open=True):
336
  system_prompt = gr.Textbox(
337
  label="System Prompt",
338
- placeholder="You are a helpful assistant...",
339
- lines=3,
340
- value="You are Step-3, a helpful AI assistant created by StepFun."
341
  )
342
-
343
- temperature = gr.Slider(
344
  minimum=0,
345
- maximum=2,
346
  value=0.7,
347
  step=0.1,
348
  label="Temperature"
349
  )
350
-
351
- max_tokens = gr.Slider(
352
- minimum=1,
353
- maximum=4096,
354
- value=2048,
355
- step=1,
356
  label="Max Tokens"
357
  )
358
-
359
- top_p = gr.Slider(
360
  minimum=0,
361
  maximum=1,
362
  value=0.95,
363
- step=0.01,
364
  label="Top P"
365
  )
366
-
367
-
368
-
369
- # 事件处理函数
370
- def user_submit(message, history, images):
371
- """用户提交消息时的处理"""
372
- print(f"[DEBUG] user_submit called with message: {message[:50] if message else 'None'}...")
373
- print(f"[DEBUG] user_submit called with images: {len(images) if images else 0} files")
374
- if message or images:
375
- # 清空输入,保存消息和图片用于后续处理
376
- return gr.update(value=""), history, gr.update(value=None), message, images
377
- return gr.update(value=message), history, gr.update(value=images), message, images
378
-
379
- def bot_response(history, saved_message, saved_images, system_prompt, temperature, max_tokens, top_p):
380
- """生成机器人响应"""
381
- print(f"[DEBUG] bot_response called with saved_message: {saved_message[:50] if saved_message else 'None'}...")
382
- print(f"[DEBUG] bot_response called with saved_images: {len(saved_images) if saved_images else 0} files")
383
- if saved_message or saved_images:
384
- # 使用生成器处理消息
385
- for updated_history in process_message(saved_message, history, saved_images, system_prompt, temperature, max_tokens, top_p):
386
- yield updated_history
387
- else:
388
- yield history
389
-
390
- def undo_last(history):
391
- if history:
392
- return history[:-1]
393
- return history
394
-
395
- def retry_last(history):
396
- if history and history[-1][0]:
397
- last_message = history[-1][0]
398
- new_history = history[:-1]
399
- return new_history, last_message
400
- return history, ""
401
 
402
- # 创建隐藏的组件来存储消息和图片
403
- saved_msg = gr.State("")
404
- saved_imgs = gr.State([])
405
-
406
- # 提交消息 - Enter键
407
- msg.submit(
408
  user_submit,
409
  [msg, chatbot, image_input],
410
  [msg, chatbot, image_input, saved_msg, saved_imgs],
411
  queue=False
412
  ).then(
413
  bot_response,
414
- [chatbot, saved_msg, saved_imgs, system_prompt, temperature, max_tokens, top_p],
415
  chatbot
416
  )
417
 
418
- # 提交消息 - Send按钮
419
  submit_btn.click(
420
  user_submit,
421
  [msg, chatbot, image_input],
@@ -423,55 +426,38 @@ with gr.Blocks(title="Step-3", theme=gr.themes.Soft(), css=css) as demo:
423
  queue=False
424
  ).then(
425
  bot_response,
426
- [chatbot, saved_msg, saved_imgs, system_prompt, temperature, max_tokens, top_p],
427
  chatbot
428
  )
429
 
430
- # 清空对话
431
- clear_btn.click(
432
- lambda: ([], "", None),
433
- None,
434
- [chatbot, msg, image_input]
435
- )
436
 
437
- # 撤销最后一条
438
  undo_btn.click(
439
- undo_last,
440
  chatbot,
441
- chatbot
 
442
  )
443
 
444
- # 重试最后一条
445
  retry_btn.click(
446
- retry_last,
447
  chatbot,
448
- [chatbot, saved_msg]
 
449
  ).then(
450
  bot_response,
451
- [chatbot, saved_msg, saved_imgs, system_prompt, temperature, max_tokens, top_p],
452
  chatbot
453
  )
454
-
455
- # 页脚
456
- gr.Markdown("""
457
- ---
458
- <div style="text-align: center; color: #666;">
459
- <p>Powered by <a href="https://www.stepfun.com/" target="_blank" style="color: #0969da;">StepFun</a> |
460
- Model: Step-3 |
461
- <a href="https://github.com/stepfun-ai" target="_blank" style="color: #0969da;">GitHub</a></p>
462
- </div>
463
- """)
464
 
465
  # 启动应用
466
  if __name__ == "__main__":
467
  print(f"[DEBUG] Starting app with API key: {'Set' if STEP_API_KEY else 'Not set'}")
468
  print(f"[DEBUG] Base URL: {BASE_URL}")
469
-
470
- demo.queue(max_size=20)
471
  demo.launch(
472
  server_name="0.0.0.0",
473
  server_port=7860,
474
  share=False,
475
- debug=False,
476
- show_error=True
477
  )
 
1
  import gradio as gr
 
2
  import base64
3
  from openai import OpenAI
 
 
4
  from PIL import Image
5
+ import io
6
+ import os
7
+ import time
8
+ import traceback
9
 
10
+ # API配置
11
  BASE_URL = "https://api.stepfun.com/v1"
12
+ STEP_API_KEY = os.environ.get("STEP_API_KEY", "5GTbxYn2RDN9qmm3Y2T2yhuzlJNrNj65y0W9dVVNrOUmD7eLB3aJ2NDXGyyl2Yccq")
13
 
14
+ print(f"[DEBUG] Starting app with API key: {'Set' if STEP_API_KEY else 'Not set'}")
15
+ print(f"[DEBUG] Base URL: {BASE_URL}")
16
+
17
+ def image_to_base64(image_path):
18
+ """将图片文件转换为base64字符串"""
19
+ try:
20
+ with Image.open(image_path) as img:
21
+ # 如果是RGBA,转换为RGB
22
+ if img.mode == 'RGBA':
23
+ rgb_img = Image.new('RGB', img.size, (255, 255, 255))
24
+ rgb_img.paste(img, mask=img.split()[3])
25
+ img = rgb_img
26
+
27
+ # 转换为字节流
28
+ buffered = io.BytesIO()
29
+ img.save(buffered, format="JPEG", quality=95)
30
+ return base64.b64encode(buffered.getvalue()).decode('utf-8')
31
+ except Exception as e:
32
+ print(f"[ERROR] Failed to convert image: {e}")
33
  return None
 
 
 
 
 
 
 
 
 
 
 
34
 
35
+ def user_submit(message, history, images):
36
+ """处理用户提交"""
 
 
 
37
  if not message and not images:
38
+ return message, history, images, "", None
 
 
39
 
40
+ # 创建用户消息显示
41
+ display_message = message if message else ""
42
+ if images:
43
+ # 显示上传的图片数量
44
+ if isinstance(images, list):
45
+ num_images = len(images)
46
+ image_text = f"[{num_images} Image{'s' if num_images > 1 else ''}]"
47
  else:
48
+ image_text = "[1 Image]"
49
+ display_message = f"{image_text} {display_message}" if display_message else image_text
 
 
50
 
51
+ history = history + [[display_message, None]]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
+ # 返回清空的输入框、更新的历史、清空的图片,以及保存的消息和图片
54
+ return "", history, None, message, images
55
+
56
+ def bot_response(history, saved_message, saved_images, system_prompt, temperature, max_tokens, top_p):
57
+ """生成机器人回复"""
58
+ if saved_message or saved_images:
59
+ # 调用process_message并流式返回结果
60
+ for updated_history in process_message(
61
+ saved_message,
62
+ history,
63
+ saved_images,
64
+ system_prompt,
65
+ temperature,
66
+ max_tokens,
67
+ top_p
68
+ ):
69
+ yield updated_history
70
  else:
71
+ yield history
72
+
73
+ def process_message(message, history, images, system_prompt, temperature, max_tokens, top_p):
74
+ """处理消息并调用Step-3 API"""
 
 
 
 
75
 
76
+ print(f"[DEBUG] Processing message: {message[:100] if message else 'None'}")
77
+ print(f"[DEBUG] Has images: {images is not None}")
78
+ print(f"[DEBUG] Images type: {type(images)}")
79
+ if images:
80
+ print(f"[DEBUG] Images content: {images}")
81
 
82
+ if not message and not images:
83
+ history[-1][1] = "Please provide a message or image."
84
+ yield history
85
+ return
 
 
 
 
 
86
 
87
+ # 确保历史记录中有用户消息
88
+ if not history or history[-1][1] is not None:
89
+ display_message = message if message else ""
90
+ if images:
91
+ if isinstance(images, list):
92
+ num_images = len(images)
93
+ image_text = f"[{num_images} Image{'s' if num_images > 1 else ''}]"
94
+ else:
95
+ image_text = "[1 Image]"
96
+ display_message = f"{image_text} {display_message}" if display_message else image_text
97
+ history.append([display_message, None])
 
 
 
 
98
 
99
+ # 开始生成回复
100
+ history[-1][1] = "🤔 Thinking..."
101
+ yield history
102
 
 
103
  try:
104
+ # 构建消息内容
105
+ content = []
106
+
107
+ # 处理图片(支持多图)
108
+ if images:
109
+ # 确保images是列表
110
+ image_list = images if isinstance(images, list) else [images]
111
+
112
+ for image_path in image_list:
113
+ if image_path:
114
+ print(f"[DEBUG] Processing image: {image_path}")
115
+ base64_image = image_to_base64(image_path)
116
+ if base64_image:
117
+ content.append({
118
+ "type": "image_url",
119
+ "image_url": {
120
+ "url": f"data:image/jpeg;base64,{base64_image}",
121
+ "detail": "high"
122
+ }
123
+ })
124
+ print(f"[DEBUG] Successfully added image to content")
125
+ else:
126
+ print(f"[ERROR] Failed to convert image: {image_path}")
127
+
128
+ # 添加文本消息
129
+ if message:
130
+ content.append({
131
+ "type": "text",
132
+ "text": message
133
+ })
134
+ print(f"[DEBUG] Added text to content: {message[:100]}")
135
+
136
+ if not content:
137
+ history[-1][1] = "❌ No valid input provided."
138
+ yield history
139
+ return
140
+
141
+ # 构造API消息
142
+ messages = []
143
+
144
+ # 添加系统提示(如果有)
145
+ if system_prompt:
146
+ messages.append({"role": "system", "content": system_prompt})
147
+
148
+ # 只使用用户消息内容,不包括之前的历史
149
+ messages.append({
150
+ "role": "user",
151
+ "content": content
152
+ })
153
+
154
+ print(f"[DEBUG] Prepared {len(messages)} messages for API")
155
+ print(f"[DEBUG] Message structure: {[{'role': m['role'], 'content_types': [c.get('type', 'text') for c in m['content']] if isinstance(m['content'], list) else 'text'} for m in messages]}")
156
+
157
+ # 处理代理问题 - 确保删除所有代理相关���环境变量
158
  import os
159
+ import httpx
160
+
161
+ # 删除所有可能的代理环境变量
162
+ proxy_vars = ['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy',
163
  'ALL_PROXY', 'all_proxy', 'NO_PROXY', 'no_proxy']
164
  for var in proxy_vars:
165
  if var in os.environ:
 
169
  # 尝试创建客户端
170
  try:
171
  # 方法1:直接创建
172
+ client = OpenAI(
173
+ api_key=STEP_API_KEY,
174
+ base_url=BASE_URL
175
+ )
176
  print("[DEBUG] Client created successfully (method 1)")
177
  except TypeError as e:
178
  if 'proxies' in str(e):
179
  print(f"[DEBUG] Method 1 failed with proxy error, trying method 2")
180
+ # 方法2:使用自定义HTTP客户端
 
181
  http_client = httpx.Client(trust_env=False)
182
  client = OpenAI(
183
+ api_key=STEP_API_KEY,
184
  base_url=BASE_URL,
185
  http_client=http_client
186
  )
 
188
  else:
189
  raise e
190
 
191
+ print(f"[DEBUG] Making API call to {BASE_URL}")
192
+
193
+ # 调用API
194
  response = client.chat.completions.create(
195
  model="step-3",
196
  messages=messages,
 
200
  stream=True
201
  )
202
 
203
+ print("[DEBUG] API call successful, starting streaming")
204
 
205
  # 流式输出
206
  full_response = ""
 
207
  in_reasoning = False
208
  reasoning_content = ""
209
  final_content = ""
210
 
211
  for chunk in response:
212
+ if chunk.choices and chunk.choices[0].delta:
213
+ delta_content = chunk.choices[0].delta.content
214
+ if delta_content:
215
+ full_response += delta_content
 
 
216
 
217
+ # 检测reasoning标签
218
+ if '<reasoning>' in full_response and not in_reasoning:
219
  in_reasoning = True
220
+ parts = full_response.split('<reasoning>')
 
 
221
  if len(parts) > 1:
222
+ reasoning_content = parts[1]
223
+
224
+ if in_reasoning and '</reasoning>' in full_response:
 
 
 
225
  in_reasoning = False
226
+ parts = full_response.split('</reasoning>')
227
  if len(parts) > 1:
228
+ reasoning_content = parts[0].split('<reasoning>')[-1]
229
+ final_content = parts[1]
230
  elif in_reasoning:
231
+ reasoning_content = full_response.split('<reasoning>')[-1]
232
+ elif '</reasoning>' in full_response:
233
+ parts = full_response.split('</reasoning>')
234
+ if len(parts) > 1:
235
+ final_content = parts[1]
236
  else:
237
+ # 没有reasoning标签的情况
238
+ if '<reasoning>' not in full_response:
239
+ final_content = full_response
240
 
241
+ # 格式化显示
242
  if reasoning_content and final_content:
243
+ display_text = f"💭 **Chain of Thought:**\n\n{reasoning_content.strip()}\n\n---\n\n📝 **Answer:**\n\n{final_content.strip()}"
 
244
  elif reasoning_content:
245
+ display_text = f"💭 **Chain of Thought:**\n\n{reasoning_content.strip()}\n\n---\n\n📝 **Answer:**\n\n*Generating...*"
 
246
  else:
 
247
  display_text = full_response
248
 
249
  history[-1][1] = display_text
 
 
 
250
  yield history
251
 
 
 
252
  # 最终格式化
253
+ if reasoning_content or final_content:
254
+ final_display = f"💭 **Chain of Thought:**\n\n{reasoning_content.strip()}\n\n---\n\n📝 **Answer:**\n\n{final_content.strip()}"
 
255
  history[-1][1] = final_display
256
+ else:
257
+ history[-1][1] = full_response
258
+
259
+ print(f"[DEBUG] Streaming completed. Response length: {len(full_response)}")
260
+ yield history
261
 
 
 
 
 
 
262
  except Exception as e:
263
+ error_msg = f" Error: {str(e)}"
264
+ print(f"[ERROR] {error_msg}")
265
  traceback.print_exc()
266
  history[-1][1] = f"❌ Error: {str(e)}"
267
  yield history
268
 
269
  # 创建Gradio界面
270
  css = """
271
+ /* 设置Box容器高度 */
272
+ .compact-file-box {
273
  height: 52px !important;
274
+ max-height: 52px !important;
275
+ padding: 0 !important;
276
+ border: none !important;
277
+ background: transparent !important;
278
  }
279
+
280
+ /* 设置File组件高度 */
281
+ #image-upload {
282
  height: 52px !important;
283
+ max-height: 52px !important;
 
284
  }
285
+
286
+ #image-upload > div {
287
+ height: 52px !important;
288
+ max-height: 52px !important;
289
  }
290
+
291
+ #image-upload .wrap {
292
  height: 52px !important;
293
+ max-height: 52px !important;
294
  padding: 0 !important;
295
  }
296
+
297
+ #image-upload .block {
298
+ height: 52px !important;
299
  max-height: 52px !important;
 
 
 
300
  }
301
+
302
+ /* 文件上传按钮 */
303
+ #image-upload button {
304
+ height: 48px !important;
305
+ font-size: 13px !important;
306
+ padding: 0 16px !important;
307
+ }
308
+
309
+ /* 文件预览区域 */
310
+ #image-upload .file-preview {
311
+ max-height: 48px !important;
312
  font-size: 12px !important;
313
+ overflow-y: auto !important;
314
  }
315
+
316
+ /* 隐藏不必要的元素 */
317
+ #image-upload label {
318
+ display: none !important;
319
  }
320
+
321
+ /* 文本框样式对齐参考 */
322
+ #message-textbox textarea {
323
  min-height: 52px !important;
 
 
 
324
  }
325
  """
326
 
 
331
  Welcome to Step-3, an advanced multimodal AI assistant by <a href="https://stepfun.com/" target="_blank" style="color: #0969da;">StepFun</a>.
332
  """)
333
 
334
+ # 创建状态变量来保存消息和图片
335
+ saved_msg = gr.State("")
336
+ saved_imgs = gr.State([])
337
+
338
  with gr.Row():
339
  with gr.Column(scale=3):
 
340
  chatbot = gr.Chatbot(
341
  height=600,
342
  show_label=False,
 
358
  elem_id="message-textbox"
359
  )
360
  with gr.Column(scale=2):
361
+ with gr.Box(elem_classes="compact-file-box"):
362
+ image_input = gr.File(
363
+ label="Upload Images",
364
+ file_count="multiple",
365
+ file_types=[".png", ".jpg", ".jpeg", ".gif", ".webp"],
366
+ interactive=True,
367
+ show_label=False,
368
+ elem_classes="compact-file",
369
+ elem_id="image-upload"
370
+ )
371
  with gr.Column(scale=1, min_width=100):
372
  submit_btn = gr.Button("Send", variant="primary")
373
 
 
379
 
380
  with gr.Column(scale=1):
381
  # 设置面板
382
+ with gr.Accordion("⚙️ Settings", open=False):
383
  system_prompt = gr.Textbox(
384
  label="System Prompt",
385
+ placeholder="Set a system prompt (optional)",
386
+ lines=3
 
387
  )
388
+ temperature_slider = gr.Slider(
 
389
  minimum=0,
390
+ maximum=1,
391
  value=0.7,
392
  step=0.1,
393
  label="Temperature"
394
  )
395
+ max_tokens_slider = gr.Slider(
396
+ minimum=100,
397
+ maximum=8000,
398
+ value=2000,
399
+ step=100,
 
400
  label="Max Tokens"
401
  )
402
+ top_p_slider = gr.Slider(
 
403
  minimum=0,
404
  maximum=1,
405
  value=0.95,
406
+ step=0.05,
407
  label="Top P"
408
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
 
410
+ # 事件处理
411
+ submit_event = msg.submit(
 
 
 
 
412
  user_submit,
413
  [msg, chatbot, image_input],
414
  [msg, chatbot, image_input, saved_msg, saved_imgs],
415
  queue=False
416
  ).then(
417
  bot_response,
418
+ [chatbot, saved_msg, saved_imgs, system_prompt, temperature_slider, max_tokens_slider, top_p_slider],
419
  chatbot
420
  )
421
 
 
422
  submit_btn.click(
423
  user_submit,
424
  [msg, chatbot, image_input],
 
426
  queue=False
427
  ).then(
428
  bot_response,
429
+ [chatbot, saved_msg, saved_imgs, system_prompt, temperature_slider, max_tokens_slider, top_p_slider],
430
  chatbot
431
  )
432
 
433
+ clear_btn.click(lambda: None, None, chatbot, queue=False)
 
 
 
 
 
434
 
 
435
  undo_btn.click(
436
+ lambda h: h[:-1] if h else h,
437
  chatbot,
438
+ chatbot,
439
+ queue=False
440
  )
441
 
 
442
  retry_btn.click(
443
+ lambda h: h[:-1] if h and h[-1][1] is not None else h,
444
  chatbot,
445
+ chatbot,
446
+ queue=False
447
  ).then(
448
  bot_response,
449
+ [chatbot, saved_msg, saved_imgs, system_prompt, temperature_slider, max_tokens_slider, top_p_slider],
450
  chatbot
451
  )
 
 
 
 
 
 
 
 
 
 
452
 
453
  # 启动应用
454
  if __name__ == "__main__":
455
  print(f"[DEBUG] Starting app with API key: {'Set' if STEP_API_KEY else 'Not set'}")
456
  print(f"[DEBUG] Base URL: {BASE_URL}")
457
+ demo.queue(max_size=10)
 
458
  demo.launch(
459
  server_name="0.0.0.0",
460
  server_port=7860,
461
  share=False,
462
+ debug=False
 
463
  )