sriting commited on
Commit
e49c2da
·
1 Parent(s): a892bc4

feat: add thinking UI

Browse files
Files changed (4) hide show
  1. app.py +286 -63
  2. assets/minimax-logo.png +0 -0
  3. requirements.txt +5 -1
  4. start.sh +16 -0
app.py CHANGED
@@ -5,6 +5,14 @@ import mimetypes
5
  import os
6
  import requests
7
  import time
 
 
 
 
 
 
 
 
8
 
9
  MODEL_VERSION = os.environ['MODEL_VERSION']
10
  API_URL = os.environ['API_URL']
@@ -17,61 +25,31 @@ NAME_MAP = {
17
  'user': os.environ.get('USER_NAME'),
18
  }
19
 
 
20
 
21
- def respond(
22
- message,
23
- history,
24
- max_tokens,
25
- temperature,
26
- top_p,
27
- ):
28
- messages = []
29
- if SYSTEM_PROMPT is not None:
30
- messages.append({
31
- 'role': 'system',
32
- 'content': SYSTEM_PROMPT,
33
- })
34
- for val in history:
35
- messages.append({
36
- 'role': val['role'],
37
- 'content': convert_content(val['content']),
38
- })
39
- messages.append({
40
- 'role': 'user',
41
- 'content': convert_content(message),
42
- })
43
- for message in messages:
44
- add_name_for_message(message)
45
 
46
- data = {
47
- 'model': MODEL_VERSION,
48
- 'messages': messages,
49
- 'stream': True,
50
- 'max_tokens': max_tokens,
51
- 'temperature': temperature,
52
- 'top_p': top_p,
53
- }
54
- r = requests.post(
55
- API_URL,
56
- headers={
57
- 'Content-Type': 'application/json',
58
- 'Authorization': 'Bearer {}'.format(API_KEY),
59
- },
60
- data=json.dumps(data),
61
- stream=True,
62
- )
63
- reply = ''
64
- for row in r.iter_lines():
65
- if row.startswith(b'data:'):
66
- data = json.loads(row[5:])
67
- if 'choices' not in data:
68
- raise gr.Error('request failed')
69
- choice = data['choices'][0]
70
- if 'delta' in choice:
71
- reply += choice['delta']['content']
72
- yield reply
73
- elif 'message' in choice:
74
- yield choice['message']['content']
75
 
76
 
77
  def add_name_for_message(message):
@@ -120,16 +98,261 @@ def encode_base64(path):
120
  )
121
 
122
 
123
- demo = gr.ChatInterface(
124
- respond,
125
- multimodal=MULTIMODAL_FLAG == 'ON',
126
- type='messages',
127
- additional_inputs=[
128
- gr.Slider(minimum=1, maximum=1000000, value=MODEL_CONTROL_DEFAULTS['tokens_to_generate'], step=1, label='Tokens to generate'),
129
- gr.Slider(minimum=0.1, maximum=1.0, value=MODEL_CONTROL_DEFAULTS['temperature'], step=0.05, label='Temperature'),
130
- gr.Slider(minimum=0.1, maximum=1.0, value=MODEL_CONTROL_DEFAULTS['top_p'], step=0.05, label='Top-p (nucleus sampling)'),
131
- ],
132
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
 
135
  if __name__ == '__main__':
 
5
  import os
6
  import requests
7
  import time
8
+ import modelscope_studio.components.antd as antd
9
+ import modelscope_studio.components.antdx as antdx
10
+ import modelscope_studio.components.base as ms
11
+ import modelscope_studio.components.pro as pro
12
+ from modelscope_studio.components.pro.chatbot import (
13
+ ChatbotActionConfig, ChatbotBotConfig, ChatbotMarkdownConfig,
14
+ ChatbotPromptsConfig, ChatbotUserConfig, ChatbotWelcomeConfig)
15
+
16
 
17
  MODEL_VERSION = os.environ['MODEL_VERSION']
18
  API_URL = os.environ['API_URL']
 
25
  'user': os.environ.get('USER_NAME'),
26
  }
27
 
28
+ MODEL_NAME = 'MiniMax-M1'
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ def prompt_select(e: gr.EventData):
32
+ return gr.update(value=e._data["payload"][0]["value"]["description"])
33
+
34
+
35
+ def clear():
36
+ return gr.update(value=None)
37
+
38
+
39
+ def retry(chatbot_value, e: gr.EventData):
40
+ index = e._data["payload"][0]["index"]
41
+ chatbot_value = chatbot_value[:index]
42
+
43
+ yield gr.update(loading=True), gr.update(value=chatbot_value), gr.update(disabled=True)
44
+ for chunk in submit(None, chatbot_value):
45
+ yield chunk
46
+
47
+
48
+ def cancel(chatbot_value):
49
+ chatbot_value[-1]["loading"] = False
50
+ chatbot_value[-1]["status"] = "done"
51
+ chatbot_value[-1]["footer"] = "Chat completion paused"
52
+ return gr.update(value=chatbot_value), gr.update(loading=False), gr.update(disabled=False)
 
 
 
 
 
 
 
53
 
54
 
55
  def add_name_for_message(message):
 
98
  )
99
 
100
 
101
+ def format_history(history):
102
+ """Convert chatbot history format to API call format"""
103
+ messages = []
104
+ if SYSTEM_PROMPT is not None:
105
+ messages.append({
106
+ 'role': 'system',
107
+ 'content': SYSTEM_PROMPT,
108
+ })
109
+
110
+ for item in history:
111
+ if item["role"] == "user":
112
+ messages.append({
113
+ 'role': 'user',
114
+ 'content': convert_content(item["content"]),
115
+ })
116
+ elif item["role"] == "assistant":
117
+ # Extract reasoning content and main content
118
+ reasoning_content = ""
119
+ main_content = ""
120
+
121
+ if isinstance(item["content"], list):
122
+ for content_item in item["content"]:
123
+ if content_item.get("type") == "tool":
124
+ reasoning_content = content_item.get("content", "")
125
+ elif content_item.get("type") == "text":
126
+ main_content = content_item.get("content", "")
127
+ else:
128
+ main_content = item["content"]
129
+
130
+ messages.append({
131
+ 'role': 'assistant',
132
+ 'content': convert_content(main_content),
133
+ 'reasoning_content': convert_content(reasoning_content),
134
+ })
135
+
136
+ return messages
137
+
138
+
139
+ def submit(sender_value, chatbot_value):
140
+ if sender_value is not None:
141
+ chatbot_value.append({
142
+ "role": "user",
143
+ "content": sender_value,
144
+ })
145
+
146
+ api_messages = format_history(chatbot_value)
147
+
148
+ for message in api_messages:
149
+ add_name_for_message(message)
150
+
151
+ chatbot_value.append({
152
+ "role": "assistant",
153
+ "content": [],
154
+ "loading": True,
155
+ "status": "pending"
156
+ })
157
+
158
+ yield {
159
+ sender: gr.update(value=None, loading=True),
160
+ clear_btn: gr.update(disabled=True),
161
+ chatbot: gr.update(value=chatbot_value)
162
+ }
163
+
164
+ try:
165
+ data = {
166
+ 'model': MODEL_VERSION,
167
+ 'messages': api_messages,
168
+ 'stream': True,
169
+ 'max_tokens': MODEL_CONTROL_DEFAULTS['tokens_to_generate'],
170
+ 'temperature': MODEL_CONTROL_DEFAULTS['temperature'],
171
+ 'top_p': MODEL_CONTROL_DEFAULTS['top_p'],
172
+ }
173
+
174
+ r = requests.post(
175
+ API_URL,
176
+ headers={
177
+ 'Content-Type': 'application/json',
178
+ 'Authorization': 'Bearer {}'.format(API_KEY),
179
+ },
180
+ data=json.dumps(data),
181
+ stream=True,
182
+ )
183
+
184
+ thought_done = False
185
+ start_time = time.time()
186
+ message_content = chatbot_value[-1]["content"]
187
+
188
+ # Reasoning content (tool type)
189
+ message_content.append({
190
+ "copyable": False,
191
+ "editable": False,
192
+ "type": "tool",
193
+ "content": "",
194
+ "options": {
195
+ "title": "🤔 Thinking..."
196
+ }
197
+ })
198
+
199
+ # Main content (text type)
200
+ message_content.append({
201
+ "type": "text",
202
+ "content": "",
203
+ })
204
+
205
+ reasoning_start_time = None
206
+ reasoning_duration = None
207
+
208
+ for row in r.iter_lines():
209
+ if row.startswith(b'data:'):
210
+ data = json.loads(row[5:])
211
+ if 'choices' not in data:
212
+ raise gr.Error('request failed')
213
+ choice = data['choices'][0]
214
+
215
+ if 'delta' in choice:
216
+ delta = choice['delta']
217
+ reasoning_content = delta.get('reasoning_content', '')
218
+ content = delta.get('content', '')
219
+
220
+ chatbot_value[-1]["loading"] = False
221
+
222
+ # Handle reasoning content
223
+ if reasoning_content:
224
+ if reasoning_start_time is None:
225
+ reasoning_start_time = time.time()
226
+ message_content[-2]["content"] += reasoning_content
227
+
228
+ # Handle main content
229
+ if content:
230
+ message_content[-1]["content"] += content
231
+
232
+ if not thought_done:
233
+ thought_done = True
234
+ if reasoning_start_time is not None:
235
+ reasoning_duration = time.time() - reasoning_start_time
236
+ thought_cost_time = "{:.2f}".format(reasoning_duration)
237
+ else:
238
+ reasoning_duration = 0.0
239
+ thought_cost_time = "0.00"
240
+ message_content[-2]["options"]["title"] = f"End of Thought ({thought_cost_time}s)"
241
+ message_content[-2]["options"]["status"] = "done"
242
+
243
+ yield {chatbot: gr.update(value=chatbot_value)}
244
+
245
+ elif 'message' in choice:
246
+ message_data = choice['message']
247
+ reasoning_content = message_data.get('reasoning_content', '')
248
+ main_content = message_data.get('content', '')
249
+
250
+ message_content[-2]["content"] = reasoning_content
251
+ message_content[-1]["content"] = main_content
252
+
253
+ if reasoning_content and main_content:
254
+ if reasoning_duration is None:
255
+ if reasoning_start_time is not None:
256
+ reasoning_duration = time.time() - reasoning_start_time
257
+ thought_cost_time = "{:.2f}".format(reasoning_duration)
258
+ else:
259
+ reasoning_duration = 0.0
260
+ thought_cost_time = "0.00"
261
+ else:
262
+ thought_cost_time = "{:.2f}".format(reasoning_duration)
263
+ message_content[-2]["options"]["title"] = f"End of Thought ({thought_cost_time}s)"
264
+ message_content[-2]["options"]["status"] = "done"
265
+
266
+ chatbot_value[-1]["loading"] = False
267
+ yield {chatbot: gr.update(value=chatbot_value)}
268
+
269
+ chatbot_value[-1]["footer"] = "{:.2f}s".format(time.time() - start_time)
270
+ chatbot_value[-1]["status"] = "done"
271
+ yield {
272
+ clear_btn: gr.update(disabled=False),
273
+ sender: gr.update(loading=False),
274
+ chatbot: gr.update(value=chatbot_value),
275
+ }
276
+
277
+ except Exception as e:
278
+ chatbot_value[-1]["loading"] = False
279
+ chatbot_value[-1]["status"] = "done"
280
+ chatbot_value[-1]["content"] = "Request failed, please try again."
281
+ yield {
282
+ clear_btn: gr.update(disabled=False),
283
+ sender: gr.update(loading=False),
284
+ chatbot: gr.update(value=chatbot_value),
285
+ }
286
+ raise e
287
+
288
+
289
+ with gr.Blocks() as demo, ms.Application(), antdx.XProvider():
290
+ with antd.Flex(vertical=True, gap="middle"):
291
+ chatbot = pro.Chatbot(
292
+ height="calc(100vh - 200px)",
293
+ welcome_config=ChatbotWelcomeConfig(
294
+ variant="borderless",
295
+ icon="./assets/minimax-logo.png",
296
+ title="Hello, I'm MiniMax-M1",
297
+ description="You can input text to get started.",
298
+ prompts=ChatbotPromptsConfig(
299
+ title="How can I help you today?",
300
+ styles={
301
+ "list": {
302
+ "width": '100%',
303
+ },
304
+ "item": {
305
+ "flex": 1,
306
+ },
307
+ },
308
+ items=[{
309
+ "label": "🤔 Logical Reasoning",
310
+ "children": [{
311
+ "description": "A is taller than B, B is shorter than C. Who is taller, A or C?"
312
+ }, {
313
+ "description": "Alice put candy in the drawer and went out. Bob moved the candy to the cabinet. Where will Alice look for the candy when she returns?"
314
+ }]
315
+ }, {
316
+ "label": "📚 Knowledge Q&A",
317
+ "children": [{
318
+ "description": "Can you tell me about middle school mathematics?"
319
+ }, {
320
+ "description": "If Earth's gravity suddenly halved, what would happen to the height humans can jump?"
321
+ }]
322
+ }])),
323
+ user_config=ChatbotUserConfig(actions=["copy", "edit"]),
324
+ bot_config=ChatbotBotConfig(
325
+ header=MODEL_NAME,
326
+ avatar="./assets/minimax-logo.png",
327
+ actions=["copy", "retry"]
328
+ ),
329
+ )
330
+
331
+ with antdx.Sender() as sender:
332
+ with ms.Slot("prefix"):
333
+ with antd.Button(value=None, color="default", variant="text") as clear_btn:
334
+ with ms.Slot("icon"):
335
+ antd.Icon("ClearOutlined")
336
+
337
+ clear_btn.click(fn=clear, outputs=[chatbot])
338
+ submit_event = sender.submit(
339
+ fn=submit,
340
+ inputs=[sender, chatbot],
341
+ outputs=[sender, chatbot, clear_btn]
342
+ )
343
+ sender.cancel(
344
+ fn=cancel,
345
+ inputs=[chatbot],
346
+ outputs=[chatbot, sender, clear_btn],
347
+ cancels=[submit_event],
348
+ queue=False
349
+ )
350
+ chatbot.retry(
351
+ fn=retry,
352
+ inputs=[chatbot],
353
+ outputs=[sender, chatbot, clear_btn]
354
+ )
355
+ chatbot.welcome_prompt_select(fn=prompt_select, outputs=[sender])
356
 
357
 
358
  if __name__ == '__main__':
assets/minimax-logo.png ADDED
requirements.txt CHANGED
@@ -1 +1,5 @@
1
- huggingface_hub==0.25.2
 
 
 
 
 
1
+ huggingface_hub==0.25.2
2
+ gradio
3
+ requests
4
+ mimetypes
5
+ modelscope_studio
start.sh ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # 设置环境变量
4
+ export API_URL="https://api.minimaxi.chat/v1/text/chatcompletion_v2"
5
+ export MODEL_CONTROL_DEFAULTS='{"tokens_to_generate": 40000, "temperature": 1, "top_p": 0.95}'
6
+ export MODEL_VERSION="MiniMax-Reasoning-01"
7
+ export API_KEY="your_api_key_here" # 请替换为你的实际API密钥
8
+
9
+ # 可选环境变量
10
+ export SYSTEM_PROMPT="你是小爱同学,一个有用的AI助手"
11
+ export MULTIMODAL="ON"
12
+ export SYSTEM_NAME="小爱同学"
13
+ export USER_NAME="用户"
14
+
15
+ # 启动应用
16
+ python app.py