Zenith Wang commited on
Commit
16c954d
·
1 Parent(s): b34c488

改造为多轮对话聊天机器人界面,添加StepFun logo

Browse files
Files changed (1) hide show
  1. app.py +152 -142
app.py CHANGED
@@ -27,211 +27,221 @@ def image_to_base64(image):
27
 
28
  return None
29
 
30
- def call_step_api(image, prompt, model, temperature=0.7, max_tokens=2000):
31
- """调用Step API进行分析,支持纯文本和图像+文本"""
32
 
33
- if not prompt:
34
- yield "", "❌ 请输入提示词"
35
  return
36
 
37
  if not STEP_API_KEY:
38
- yield "", "❌ API密钥未配置。请在 Hugging Face Space 的 Settings 中添加 STEP_API_KEY 环境变量。"
 
39
  return
40
 
41
- # 构造消息内容 - 参考官方示例
 
 
 
 
 
 
 
 
 
 
42
  if image is not None:
43
  # 有图片的情况
44
  try:
45
  base64_image = image_to_base64(image)
46
  if base64_image is None:
47
- yield "", "❌ 图片处理失败"
 
48
  return
49
 
50
- # 按照官方示例的格式构造消息
51
- messages = [
52
- {"role": "user", "content": [
53
- {"type": "image_url", "image_url": {"url": f"data:image/jpg;base64,{base64_image}", "detail": "high"}},
54
- {"type": "text", "text": prompt}
55
- ]}
56
  ]
 
 
 
 
 
57
  except Exception as e:
58
- yield "", f"❌ 图片处理错误: {str(e)}"
 
59
  return
60
  else:
61
- # 纯文本的情况
62
- messages = [
63
- {"role": "user", "content": prompt}
64
- ]
 
 
65
 
66
- # 创建OpenAI客户端 - 完全按照官方示例
67
  try:
68
  client = OpenAI(api_key=STEP_API_KEY, base_url=BASE_URL)
69
  except Exception as e:
70
- yield "", f"❌ 客户端初始化失败: {str(e)}"
 
71
  return
72
 
73
- # 记录开始时间
74
- start_time = time.time()
75
-
76
  try:
77
- # 调用API - 按照官方示例
78
  response = client.chat.completions.create(
79
  model=model,
80
  messages=messages,
 
 
81
  stream=True
82
  )
83
- except Exception as e:
84
- yield "", f"❌ API请求失败: {str(e)}"
85
- return
86
-
87
- # 处理流式响应
88
- full_response = ""
89
- reasoning_content = ""
90
- final_answer = ""
91
- is_reasoning = False
92
- reasoning_started = False
93
-
94
- try:
95
  for chunk in response:
96
- # 按照官方示例处理chunk
97
  if chunk.choices and len(chunk.choices) > 0:
98
  delta = chunk.choices[0].delta
99
-
100
  if hasattr(delta, 'content') and delta.content:
101
- content = delta.content
102
- full_response += content
103
-
104
- # 检测reasoning标记
105
- if "<reasoning>" in content:
106
- is_reasoning = True
107
- reasoning_started = True
108
- # 处理标记前后的内容
109
- parts = content.split("<reasoning>")
110
- if parts[0]:
111
- final_answer += parts[0]
112
- if len(parts) > 1:
113
- reasoning_content += parts[1]
114
- elif "</reasoning>" in content:
115
- # 处理结束标记
116
- parts = content.split("</reasoning>")
117
- if parts[0]:
118
- reasoning_content += parts[0]
119
- is_reasoning = False
120
- if len(parts) > 1:
121
- final_answer += parts[1]
122
- elif is_reasoning:
123
- reasoning_content += content
124
- else:
125
- final_answer += content
126
 
127
- # 实时输出
128
- yield reasoning_content, final_answer
129
-
130
  except Exception as e:
131
- yield reasoning_content, final_answer + f"\n\n流处理错误: {str(e)}"
132
- return
133
-
134
- # 添加生成时间
135
- elapsed_time = time.time() - start_time
136
- time_info = f"\n\n⏱️ 生成用时: {elapsed_time:.2f}秒"
137
- final_answer += time_info
138
-
139
- yield reasoning_content, final_answer
 
 
 
140
 
141
  # 创建Gradio界面
142
  with gr.Blocks(title="Step-3", theme=gr.themes.Soft()) as demo:
143
  gr.Markdown("""
144
  # 🤖 Step-3
 
145
  """)
146
 
147
  with gr.Row():
148
- with gr.Column(scale=1):
149
- # 输入区域
150
- image_input = gr.Image(
151
- label="上传图片(可选)",
152
- type="pil",
153
- height=300
 
 
154
  )
155
 
156
- prompt_input = gr.Textbox(
157
- label="提示词",
158
- placeholder="输入你的问题或描述...",
159
- lines=3,
160
- value=""
161
- )
 
 
 
 
 
 
 
 
 
162
 
163
- with gr.Accordion("高级设置", open=False):
164
- model_select = gr.Dropdown(
165
- choices=MODELS,
166
- value=MODELS[0],
167
- label="选择模型"
168
- )
169
-
170
- temperature_slider = gr.Slider(
171
- minimum=0,
172
- maximum=1,
173
- value=0.7,
174
- step=0.1,
175
- label="Temperature"
176
  )
177
-
178
- max_tokens_slider = gr.Slider(
179
- minimum=100,
180
- maximum=4000,
181
- value=2000,
182
- step=100,
183
- label="最大输出长度"
184
- )
185
-
186
- submit_btn = gr.Button("🚀 开始分析", variant="primary")
187
- clear_btn = gr.Button("🗑️ 清空", variant="secondary")
188
 
189
  with gr.Column(scale=1):
190
- # 推理过程展示
191
- with gr.Accordion("💭 推理过程 (CoT)", open=True):
192
- reasoning_output = gr.Textbox(
193
- label="思考过程",
194
- lines=10,
195
- max_lines=15,
196
- show_copy_button=True,
197
- interactive=False
198
- )
199
 
200
- # 最终答案展示
201
- answer_output = gr.Textbox(
202
- label="📝 分析结果",
203
- lines=15,
204
- max_lines=25,
205
- show_copy_button=True,
206
- interactive=False
207
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
 
209
- # 事件处理 - 流式输出到两个文本框
210
  submit_btn.click(
211
- fn=call_step_api,
212
- inputs=[
213
- image_input,
214
- prompt_input,
215
- model_select,
216
- temperature_slider,
217
- max_tokens_slider
218
- ],
219
- outputs=[reasoning_output, answer_output],
220
- show_progress=True
221
  )
222
 
223
  clear_btn.click(
224
- fn=lambda: (None, "", "", ""),
225
- inputs=[],
226
- outputs=[image_input, prompt_input, reasoning_output, answer_output]
 
227
  )
228
 
229
  # 页脚
230
  gr.Markdown("""
231
  ---
232
- Powered by [Step-3](https://www.stepfun.com/)
 
 
 
 
233
  """)
234
 
235
  # 启动应用
236
  if __name__ == "__main__":
 
237
  demo.launch()
 
27
 
28
  return None
29
 
30
+ def call_step_api_stream(message, history, model, temperature, max_tokens, image=None):
31
+ """调用Step API进行流式对话"""
32
 
33
+ if not message and not image:
34
+ yield history
35
  return
36
 
37
  if not STEP_API_KEY:
38
+ history.append([message or "图片", "❌ API密钥未配置。请在 Settings 中添加 STEP_API_KEY"])
39
+ yield history
40
  return
41
 
42
+ # 构造消息历史
43
+ messages = []
44
+
45
+ # 添加历史对话
46
+ for h in history:
47
+ if h[0]: # 用户消息
48
+ messages.append({"role": "user", "content": h[0]})
49
+ if h[1]: # 助手回复
50
+ messages.append({"role": "assistant", "content": h[1]})
51
+
52
+ # 构造当前消息
53
  if image is not None:
54
  # 有图片的情况
55
  try:
56
  base64_image = image_to_base64(image)
57
  if base64_image is None:
58
+ history.append([message or "图片", "❌ 图片处理失败"])
59
+ yield history
60
  return
61
 
62
+ current_content = [
63
+ {"type": "image_url", "image_url": {"url": f"data:image/jpg;base64,{base64_image}", "detail": "high"}}
 
 
 
 
64
  ]
65
+ if message:
66
+ current_content.append({"type": "text", "text": message})
67
+
68
+ messages.append({"role": "user", "content": current_content})
69
+ display_message = f"[图片] {message}" if message else "[图片]"
70
  except Exception as e:
71
+ history.append([message or "图片", f"❌ 图片处理错误: {str(e)}"])
72
+ yield history
73
  return
74
  else:
75
+ # 纯文本
76
+ messages.append({"role": "user", "content": message})
77
+ display_message = message
78
+
79
+ # 添加到历史记录
80
+ history.append([display_message, ""])
81
 
82
+ # 创建客户端
83
  try:
84
  client = OpenAI(api_key=STEP_API_KEY, base_url=BASE_URL)
85
  except Exception as e:
86
+ history[-1][1] = f"❌ 客户端初始化失败: {str(e)}"
87
+ yield history
88
  return
89
 
90
+ # 调用API
 
 
91
  try:
 
92
  response = client.chat.completions.create(
93
  model=model,
94
  messages=messages,
95
+ temperature=temperature,
96
+ max_tokens=max_tokens,
97
  stream=True
98
  )
99
+
100
+ # 处理流式响应
101
+ full_response = ""
 
 
 
 
 
 
 
 
 
102
  for chunk in response:
 
103
  if chunk.choices and len(chunk.choices) > 0:
104
  delta = chunk.choices[0].delta
 
105
  if hasattr(delta, 'content') and delta.content:
106
+ full_response += delta.content
107
+ history[-1][1] = full_response
108
+ yield history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
 
 
 
110
  except Exception as e:
111
+ history[-1][1] = f"❌ API请求失败: {str(e)}"
112
+ yield history
113
+
114
+ def user_input(message, history, image):
115
+ """处理用户输入"""
116
+ if message or image:
117
+ return "", history, None
118
+ return message, history, image
119
+
120
+ def clear_history():
121
+ """清空对话历史"""
122
+ return [], None, ""
123
 
124
  # 创建Gradio界面
125
  with gr.Blocks(title="Step-3", theme=gr.themes.Soft()) as demo:
126
  gr.Markdown("""
127
  # 🤖 Step-3
128
+ Hello, I am Step-3!
129
  """)
130
 
131
  with gr.Row():
132
+ with gr.Column(scale=3):
133
+ # 对话界面
134
+ chatbot = gr.Chatbot(
135
+ height=500,
136
+ show_label=False,
137
+ elem_id="chatbot",
138
+ bubble_full_width=False,
139
+ avatar_images=(None, "🤖")
140
  )
141
 
142
+ with gr.Row():
143
+ with gr.Column(scale=8):
144
+ msg = gr.Textbox(
145
+ label="输入消息",
146
+ placeholder="输入你的问题...",
147
+ lines=1,
148
+ max_lines=5,
149
+ show_label=False,
150
+ elem_id="msg",
151
+ container=False
152
+ )
153
+ with gr.Column(scale=1, min_width=100):
154
+ submit_btn = gr.Button("发送", variant="primary")
155
+ with gr.Column(scale=1, min_width=100):
156
+ clear_btn = gr.Button("清空对话")
157
 
158
+ # 图片上传
159
+ with gr.Row():
160
+ image_input = gr.Image(
161
+ label="上传图片(可选)",
162
+ type="pil",
163
+ height=150,
164
+ scale=1
 
 
 
 
 
 
165
  )
 
 
 
 
 
 
 
 
 
 
 
166
 
167
  with gr.Column(scale=1):
168
+ # 设置面板
169
+ gr.Markdown("### ⚙️ 设置")
170
+
171
+ model_select = gr.Dropdown(
172
+ choices=MODELS,
173
+ value=MODELS[0],
174
+ label="模型选择",
175
+ interactive=True
176
+ )
177
 
178
+ temperature_slider = gr.Slider(
179
+ minimum=0,
180
+ maximum=1,
181
+ value=0.7,
182
+ step=0.1,
183
+ label="Temperature",
184
+ interactive=True
185
  )
186
+
187
+ max_tokens_slider = gr.Slider(
188
+ minimum=100,
189
+ maximum=4000,
190
+ value=2000,
191
+ step=100,
192
+ label="最大输出长度",
193
+ interactive=True
194
+ )
195
+
196
+ gr.Markdown("""
197
+ ### 📝 使用说明
198
+ - 支持多轮对话
199
+ - 可上传图片进行分析
200
+ - 支持纯文本对话
201
+ - 历史记录会保留上下文
202
+ """)
203
+
204
+ # 事件处理
205
+ msg.submit(
206
+ user_input,
207
+ [msg, chatbot, image_input],
208
+ [msg, chatbot, image_input],
209
+ queue=False
210
+ ).then(
211
+ call_step_api_stream,
212
+ [msg, chatbot, model_select, temperature_slider, max_tokens_slider, image_input],
213
+ chatbot
214
+ )
215
 
 
216
  submit_btn.click(
217
+ user_input,
218
+ [msg, chatbot, image_input],
219
+ [msg, chatbot, image_input],
220
+ queue=False
221
+ ).then(
222
+ call_step_api_stream,
223
+ [msg, chatbot, model_select, temperature_slider, max_tokens_slider, image_input],
224
+ chatbot
 
 
225
  )
226
 
227
  clear_btn.click(
228
+ clear_history,
229
+ None,
230
+ [chatbot, image_input, msg],
231
+ queue=False
232
  )
233
 
234
  # 页脚
235
  gr.Markdown("""
236
  ---
237
+ <div style="text-align: center;">
238
+ <img src="https://huggingface.co/stepfun-ai/step3/resolve/main/figures/stepfun-logo.png" alt="StepFun Logo" style="height: 40px; margin: 10px;">
239
+ <br>
240
+ Powered by <a href="https://www.stepfun.com/" target="_blank">StepFun</a>
241
+ </div>
242
  """)
243
 
244
  # 启动应用
245
  if __name__ == "__main__":
246
+ demo.queue()
247
  demo.launch()