cherrytest commited on
Commit
785fbfa
·
1 Parent(s): 04351ab
Files changed (6) hide show
  1. README.md +11 -23
  2. app.py +882 -0
  3. assets/minimax-logo.png +0 -0
  4. config.py +104 -0
  5. requirements.txt +4 -0
  6. start.sh +16 -0
README.md CHANGED
@@ -1,25 +1,13 @@
1
  ---
2
- # 详细文档见https://modelscope.cn/docs/%E5%88%9B%E7%A9%BA%E9%97%B4%E5%8D%A1%E7%89%87
3
- domain: #领域:cv/nlp/audio/multi-modal/AutoML
4
- # - cv
5
- tags: #自定义标签
6
- -
7
- datasets: #关联数据集
8
- evaluation:
9
- #- iic/ICDAR13_HCTR_Dataset
10
- test:
11
- #- iic/MTWI
12
- train:
13
- #- iic/SIBR
14
- models: #关联模型
15
- #- iic/ofa_ocr-recognition_general_base_zh
16
-
17
- ## 启动文件(若SDK为Gradio/Streamlit,默认为app.py, 若为Static HTML, 默认为index.html)
18
- # deployspec:
19
- # entry_file: app.py
20
- license: Apache License 2.0
21
  ---
22
- #### Clone with HTTP
23
- ```bash
24
- git clone https://www.modelscope.cn/studios/MiniMax/MiniMax-M1.git
25
- ```
 
1
  ---
2
+ title: MiniMax M1
3
+ emoji: 💬
4
+ colorFrom: yellow
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: 5.0.1
8
+ app_file: app.py
9
+ pinned: false
10
+ license: apache-2.0
 
 
 
 
 
 
 
 
 
 
11
  ---
12
+
13
+ An MiniMax-M1 example chatbot using [MiniMax-M1](https://huggingface.co/spaces/MiniMaxAI/MiniMax-M1), [Gradio](https://gradio.app), [`modelscope_studio`](https://modelscope.cn/studios/modelscope/modelscope-studio).
 
 
app.py ADDED
@@ -0,0 +1,882 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import gradio as gr
3
+ import json
4
+ import mimetypes
5
+ import os
6
+ import requests
7
+ import time
8
+ import modelscope_studio.components.legacy as legacy
9
+ import modelscope_studio.components.antd as antd
10
+ import modelscope_studio.components.antdx as antdx
11
+ import modelscope_studio.components.base as ms
12
+ import modelscope_studio.components.pro as pro
13
+ from modelscope_studio.components.pro.chatbot import (
14
+ ChatbotActionConfig, ChatbotBotConfig, ChatbotMarkdownConfig,
15
+ ChatbotPromptsConfig, ChatbotUserConfig, ChatbotWelcomeConfig)
16
+ from config import DEFAULT_PROMPTS, EXAMPLES, SystemPrompt
17
+ import re
18
+
19
+
20
+ MODEL_VERSION = os.environ['MODEL_VERSION']
21
+ API_URL = os.environ['API_URL']
22
+ API_KEY = os.environ['API_KEY']
23
+ SYSTEM_PROMPT = os.environ.get('SYSTEM_PROMPT')
24
+ MULTIMODAL_FLAG = os.environ.get('MULTIMODAL')
25
+ MODEL_CONTROL_DEFAULTS = json.loads(os.environ['MODEL_CONTROL_DEFAULTS'])
26
+ NAME_MAP = {
27
+ 'system': os.environ.get('SYSTEM_NAME'),
28
+ 'user': os.environ.get('USER_NAME'),
29
+ }
30
+
31
+ MODEL_NAME = 'MiniMax-M1'
32
+
33
+
34
+ def prompt_select(e: gr.EventData):
35
+ return gr.update(value=e._data["payload"][0]["value"]["description"])
36
+
37
+
38
+ def clear():
39
+ return gr.update(value=None)
40
+
41
+
42
+ def retry(chatbot_value, e: gr.EventData):
43
+ index = e._data["payload"][0]["index"]
44
+ chatbot_value = chatbot_value[:index]
45
+
46
+ yield gr.update(loading=True), gr.update(value=chatbot_value), gr.update(disabled=True)
47
+ for chunk in submit(None, chatbot_value):
48
+ yield chunk
49
+
50
+
51
+ def cancel(chatbot_value):
52
+ chatbot_value[-1]["loading"] = False
53
+ chatbot_value[-1]["status"] = "done"
54
+ chatbot_value[-1]["footer"] = "Chat completion paused"
55
+ return gr.update(value=chatbot_value), gr.update(loading=False), gr.update(disabled=False)
56
+
57
+
58
+ def add_name_for_message(message):
59
+ name = NAME_MAP.get(message['role'])
60
+ if name is not None:
61
+ message['name'] = name
62
+
63
+
64
+ def convert_content(content):
65
+ if isinstance(content, str):
66
+ return content
67
+ if isinstance(content, tuple):
68
+ return [{
69
+ 'type': 'image_url',
70
+ 'image_url': {
71
+ 'url': encode_base64(content[0]),
72
+ },
73
+ }]
74
+ content_list = []
75
+ for key, val in content.items():
76
+ if key == 'text':
77
+ content_list.append({
78
+ 'type': 'text',
79
+ 'text': val,
80
+ })
81
+ elif key == 'files':
82
+ for f in val:
83
+ content_list.append({
84
+ 'type': 'image_url',
85
+ 'image_url': {
86
+ 'url': encode_base64(f),
87
+ },
88
+ })
89
+ return content_list
90
+
91
+
92
+ def encode_base64(path):
93
+ guess_type = mimetypes.guess_type(path)[0]
94
+ if not guess_type.startswith('image/'):
95
+ raise gr.Error('not an image ({}): {}'.format(guess_type, path))
96
+ with open(path, 'rb') as handle:
97
+ data = handle.read()
98
+ return 'data:{};base64,{}'.format(
99
+ guess_type,
100
+ base64.b64encode(data).decode(),
101
+ )
102
+
103
+
104
+ def format_history(history):
105
+ """Convert chatbot history format to API call format"""
106
+ messages = []
107
+ if SYSTEM_PROMPT is not None:
108
+ messages.append({
109
+ 'role': 'system',
110
+ 'content': SYSTEM_PROMPT,
111
+ })
112
+
113
+ for item in history:
114
+ if item["role"] == "user":
115
+ messages.append({
116
+ 'role': 'user',
117
+ 'content': convert_content(item["content"]),
118
+ })
119
+ elif item["role"] == "assistant":
120
+ # Extract reasoning content and main content
121
+ reasoning_content = ""
122
+ main_content = ""
123
+
124
+ if isinstance(item["content"], list):
125
+ for content_item in item["content"]:
126
+ if content_item.get("type") == "tool":
127
+ reasoning_content = content_item.get("content", "")
128
+ elif content_item.get("type") == "text":
129
+ main_content = content_item.get("content", "")
130
+ else:
131
+ main_content = item["content"]
132
+
133
+ messages.append({
134
+ 'role': 'assistant',
135
+ 'content': convert_content(main_content),
136
+ 'reasoning_content': convert_content(reasoning_content),
137
+ })
138
+
139
+ return messages
140
+
141
+
142
+ def submit(sender_value, chatbot_value):
143
+ if sender_value is not None:
144
+ chatbot_value.append({
145
+ "role": "user",
146
+ "content": sender_value,
147
+ })
148
+
149
+ api_messages = format_history(chatbot_value)
150
+
151
+ for message in api_messages:
152
+ add_name_for_message(message)
153
+
154
+ chatbot_value.append({
155
+ "role": "assistant",
156
+ "content": [],
157
+ "loading": True,
158
+ "status": "pending"
159
+ })
160
+
161
+ yield {
162
+ sender: gr.update(value=None, loading=True),
163
+ clear_btn: gr.update(disabled=True),
164
+ chatbot: gr.update(value=chatbot_value)
165
+ }
166
+
167
+ try:
168
+ data = {
169
+ 'model': MODEL_VERSION,
170
+ 'messages': api_messages,
171
+ 'stream': True,
172
+ 'max_tokens': MODEL_CONTROL_DEFAULTS['tokens_to_generate'],
173
+ 'temperature': MODEL_CONTROL_DEFAULTS['temperature'],
174
+ 'top_p': MODEL_CONTROL_DEFAULTS['top_p'],
175
+ }
176
+
177
+ r = requests.post(
178
+ API_URL,
179
+ headers={
180
+ 'Content-Type': 'application/json',
181
+ 'Authorization': 'Bearer {}'.format(API_KEY),
182
+ },
183
+ data=json.dumps(data),
184
+ stream=True,
185
+ )
186
+
187
+ thought_done = False
188
+ start_time = time.time()
189
+ message_content = chatbot_value[-1]["content"]
190
+
191
+ # Reasoning content (tool type)
192
+ message_content.append({
193
+ "type": "tool",
194
+ "content": "",
195
+ "options": {
196
+ "title": "🤔 Thinking..."
197
+ }
198
+ })
199
+
200
+ # Main content (text type)
201
+ message_content.append({
202
+ "type": "text",
203
+ "content": "",
204
+ })
205
+
206
+ reasoning_start_time = None
207
+ reasoning_duration = None
208
+
209
+ for row in r.iter_lines():
210
+ if row.startswith(b'data:'):
211
+ data = json.loads(row[5:])
212
+ if 'choices' not in data:
213
+ raise gr.Error('request failed')
214
+ choice = data['choices'][0]
215
+
216
+ if 'delta' in choice:
217
+ delta = choice['delta']
218
+ reasoning_content = delta.get('reasoning_content', '')
219
+ content = delta.get('content', '')
220
+
221
+ chatbot_value[-1]["loading"] = False
222
+
223
+ # Handle reasoning content
224
+ if reasoning_content:
225
+ if reasoning_start_time is None:
226
+ reasoning_start_time = time.time()
227
+ message_content[-2]["content"] += reasoning_content
228
+
229
+ # Handle main content
230
+ if content:
231
+ message_content[-1]["content"] += content
232
+
233
+ if not thought_done:
234
+ thought_done = True
235
+ if reasoning_start_time is not None:
236
+ reasoning_duration = time.time() - reasoning_start_time
237
+ thought_cost_time = "{:.2f}".format(reasoning_duration)
238
+ else:
239
+ reasoning_duration = 0.0
240
+ thought_cost_time = "0.00"
241
+ message_content[-2]["options"] = {"title": f"End of Thought ({thought_cost_time}s)"}
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
+
265
+ chatbot_value[-1]["loading"] = False
266
+ yield {chatbot: gr.update(value=chatbot_value)}
267
+
268
+ chatbot_value[-1]["footer"] = "{:.2f}s".format(time.time() - start_time)
269
+ chatbot_value[-1]["status"] = "done"
270
+ yield {
271
+ clear_btn: gr.update(disabled=False),
272
+ sender: gr.update(loading=False),
273
+ chatbot: gr.update(value=chatbot_value),
274
+ }
275
+
276
+ except Exception as e:
277
+ chatbot_value[-1]["loading"] = False
278
+ chatbot_value[-1]["status"] = "done"
279
+ chatbot_value[-1]["content"] = "Request failed, please try again."
280
+ yield {
281
+ clear_btn: gr.update(disabled=False),
282
+ sender: gr.update(loading=False),
283
+ chatbot: gr.update(value=chatbot_value),
284
+ }
285
+ raise e
286
+
287
+
288
+ def remove_code_block(text):
289
+ # Try to match code blocks with language markers
290
+ patterns = [
291
+ r'```(?:html|HTML)\n([\s\S]+?)\n```', # Match ```html or ```HTML
292
+ r'```\n([\s\S]+?)\n```', # Match code blocks without language markers
293
+ r'```([\s\S]+?)```' # Match code blocks without line breaks
294
+ ]
295
+
296
+ for pattern in patterns:
297
+ match = re.search(pattern, text, re.DOTALL)
298
+ if match:
299
+ extracted = match.group(1).strip()
300
+ print("Successfully extracted code block:", extracted)
301
+ return extracted
302
+
303
+ # If no code block is found, check if the entire text is HTML
304
+ if text.strip().startswith('<!DOCTYPE html>') or text.strip().startswith('<html'):
305
+ print("Text appears to be raw HTML, using as is")
306
+ return text.strip()
307
+
308
+ print("No code block found in text:", text)
309
+ return text.strip()
310
+
311
+ def send_to_sandbox(code):
312
+ # Add a wrapper to inject necessary permissions
313
+ wrapped_code = f"""
314
+ <!DOCTYPE html>
315
+ <html>
316
+ <head>
317
+ <meta charset="UTF-8">
318
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
319
+ <script>
320
+ // Create a safe storage alternative
321
+ const safeStorage = {{
322
+ _data: {{}},
323
+ getItem: function(key) {{
324
+ return this._data[key] || null;
325
+ }},
326
+ setItem: function(key, value) {{
327
+ this._data[key] = value;
328
+ }},
329
+ removeItem: function(key) {{
330
+ delete this._data[key];
331
+ }},
332
+ clear: function() {{
333
+ this._data = {{}};
334
+ }}
335
+ }};
336
+
337
+ // Replace native localStorage
338
+ Object.defineProperty(window, 'localStorage', {{
339
+ value: safeStorage,
340
+ writable: false
341
+ }});
342
+
343
+ // Add error handling without using alert
344
+ window.onerror = function(message, source, lineno, colno, error) {{
345
+ console.error('Error:', message);
346
+ }};
347
+ </script>
348
+ </head>
349
+ <body>
350
+ {code}
351
+ </body>
352
+ </html>
353
+ """
354
+ encoded_html = base64.b64encode(wrapped_code.encode('utf-8')).decode('utf-8')
355
+ data_uri = f"data:text/html;charset=utf-8;base64,{encoded_html}"
356
+ iframe = f'<iframe src="{data_uri}" width="100%" height="920px" sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-presentation" allow="display-capture"></iframe>'
357
+ print("Generated iframe:", iframe)
358
+ return iframe
359
+
360
+ def select_example(example):
361
+ if isinstance(example, dict):
362
+ return example.get("description", "")
363
+ return ""
364
+
365
+ def generate_code(query: str):
366
+ if not query:
367
+ return {
368
+ code_output: gr.update(value=None),
369
+ reasoning_output: gr.update(value=None),
370
+ sandbox: gr.update(value=None),
371
+ state_tab: gr.update(active_key="empty"),
372
+ output_tabs: gr.update(active_key="reasoning", visible=False),
373
+ loading: gr.update(tip="Thinking...")
374
+ }
375
+
376
+ print("Starting code generation with query:", query)
377
+ messages = [{
378
+ 'role': 'system',
379
+ 'content': SystemPrompt
380
+ }, {
381
+ 'role': 'user',
382
+ 'content': query
383
+ }]
384
+
385
+ max_retries = 3
386
+ retry_count = 0
387
+
388
+ while retry_count < max_retries:
389
+ try:
390
+ data = {
391
+ 'model': MODEL_VERSION,
392
+ 'messages': messages,
393
+ 'stream': True,
394
+ 'max_tokens': MODEL_CONTROL_DEFAULTS['tokens_to_generate'],
395
+ 'temperature': MODEL_CONTROL_DEFAULTS['temperature'],
396
+ 'top_p': MODEL_CONTROL_DEFAULTS['top_p'],
397
+ }
398
+
399
+ print(f"Attempt {retry_count + 1}: Sending request to API with data:", json.dumps(data, indent=2))
400
+ r = requests.post(
401
+ API_URL,
402
+ headers={
403
+ 'Content-Type': 'application/json',
404
+ 'Authorization': 'Bearer {}'.format(API_KEY),
405
+ },
406
+ data=json.dumps(data),
407
+ stream=True,
408
+ timeout=60 # Set 60 seconds timeout
409
+ )
410
+
411
+ content = ""
412
+ reasoning_content = ""
413
+ loading_text = "Thinking..."
414
+
415
+ for row in r.iter_lines():
416
+ if row.startswith(b'data:'):
417
+ data = json.loads(row[5:])
418
+ print("Received data from API:", json.dumps(data, indent=2))
419
+ if 'choices' not in data:
420
+ raise gr.Error('request failed')
421
+ choice = data['choices'][0]
422
+
423
+ if 'delta' in choice:
424
+ delta = choice['delta']
425
+ content += delta.get('content', '')
426
+ reasoning_content += delta.get('reasoning_content', '')
427
+
428
+ # Update loading text based on content
429
+ if content and not loading_text == "Generating code...":
430
+ loading_text = "Generating code..."
431
+ yield {
432
+ code_output: gr.update(value=content),
433
+ reasoning_output: gr.update(value=reasoning_content + "\n"),
434
+ sandbox: gr.update(value=None),
435
+ state_tab: gr.update(active_key="loading"),
436
+ output_tabs: gr.update(active_key="reasoning", visible=True),
437
+ loading: gr.update(tip=loading_text)
438
+ }
439
+ else:
440
+ yield {
441
+ code_output: gr.update(value=content),
442
+ reasoning_output: gr.update(value=reasoning_content + "\n"),
443
+ sandbox: gr.update(value=None),
444
+ state_tab: gr.update(active_key="loading"),
445
+ output_tabs: gr.update(active_key="reasoning", visible=True),
446
+ loading: gr.update(tip=loading_text)
447
+ }
448
+ elif 'message' in choice:
449
+ message_data = choice['message']
450
+ content = message_data.get('content', '')
451
+ reasoning_content = message_data.get('reasoning_content', '')
452
+ print("Final content:", content)
453
+ print("Final reasoning:", reasoning_content)
454
+ html_content = remove_code_block(content)
455
+ print("Extracted HTML:", html_content)
456
+ yield {
457
+ code_output: gr.update(value=content),
458
+ reasoning_output: gr.update(value=reasoning_content + "\n"),
459
+ sandbox: gr.update(value=send_to_sandbox(html_content)),
460
+ state_tab: gr.update(active_key="render"),
461
+ output_tabs: gr.update(active_key="code", visible=True),
462
+ loading: gr.update(tip="Done")
463
+ }
464
+
465
+ # If successful, break out of retry loop
466
+ break
467
+
468
+ except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
469
+ retry_count += 1
470
+ if retry_count == max_retries:
471
+ print(f"Failed after {max_retries} attempts:", str(e))
472
+ raise gr.Error(f"Request failed after {max_retries} attempts: {str(e)}")
473
+ print(f"Attempt {retry_count} failed, retrying...")
474
+ time.sleep(1) # Wait 1 second before retrying
475
+
476
+ except Exception as e:
477
+ print("Error occurred:", str(e))
478
+ raise gr.Error(str(e))
479
+
480
+ css = """
481
+ /* Add styles for the main container */
482
+ .ant-tabs-content {
483
+ height: calc(100vh - 200px);
484
+ overflow: hidden;
485
+ }
486
+
487
+ .ant-tabs-tabpane {
488
+ height: 100%;
489
+ overflow-y: auto;
490
+ }
491
+
492
+ /* Modify existing styles */
493
+ .output-loading {
494
+ display: flex;
495
+ flex-direction: column;
496
+ align-items: center;
497
+ justify-content: center;
498
+ width: 100%;
499
+ height: 100%;
500
+ min-height: unset;
501
+ }
502
+
503
+ .output-html {
504
+ display: flex;
505
+ flex-direction: column;
506
+ width: 100%;
507
+ height: 100%;
508
+ min-height: unset;
509
+ background: #fff;
510
+ border-radius: 8px;
511
+ border: 1px solid #e8e8e8;
512
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
513
+ overflow: hidden;
514
+ }
515
+
516
+ .right_content {
517
+ display: flex;
518
+ flex-direction: column;
519
+ align-items: center;
520
+ justify-content: center;
521
+ width: 100%;
522
+ height: 100%;
523
+ min-height: unset;
524
+ background: #fff;
525
+ border-radius: 8px;
526
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
527
+ }
528
+
529
+ /* Add styles for the code playground container */
530
+ .code-playground-container {
531
+ height: 100%;
532
+ overflow-y: auto;
533
+ padding-right: 8px;
534
+ }
535
+
536
+ .code-playground-container::-webkit-scrollbar {
537
+ width: 6px;
538
+ }
539
+
540
+ .code-playground-container::-webkit-scrollbar-track {
541
+ background: #f1f1f1;
542
+ border-radius: 3px;
543
+ }
544
+
545
+ .code-playground-container::-webkit-scrollbar-thumb {
546
+ background: #888;
547
+ border-radius: 3px;
548
+ }
549
+
550
+ .code-playground-container::-webkit-scrollbar-thumb:hover {
551
+ background: #555;
552
+ }
553
+
554
+ .render_header {
555
+ display: flex;
556
+ align-items: center;
557
+ padding: 8px 16px;
558
+ background: #f5f5f5;
559
+ border-bottom: 1px solid #e8e8e8;
560
+ border-top-left-radius: 8px;
561
+ border-top-right-radius: 8px;
562
+ }
563
+
564
+ .header_btn {
565
+ width: 12px;
566
+ height: 12px;
567
+ border-radius: 50%;
568
+ margin-right: 8px;
569
+ display: inline-block;
570
+ }
571
+
572
+ .header_btn:nth-child(1) {
573
+ background: #ff5f56;
574
+ }
575
+
576
+ .header_btn:nth-child(2) {
577
+ background: #ffbd2e;
578
+ }
579
+
580
+ .header_btn:nth-child(3) {
581
+ background: #27c93f;
582
+ }
583
+
584
+ .output-html > iframe {
585
+ flex: 1;
586
+ border: none;
587
+ background: #fff;
588
+ }
589
+
590
+ .reasoning-box {
591
+ height: 300px;
592
+ overflow-y: auto;
593
+ background-color: #f5f5f5;
594
+ border-radius: 4px;
595
+ margin-bottom: 12px;
596
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
597
+ font-size: 14px;
598
+ line-height: 1.2;
599
+ white-space: pre-wrap;
600
+ word-break: break-word;
601
+ width: 100%;
602
+ box-sizing: border-box;
603
+ scroll-behavior: smooth;
604
+ }
605
+
606
+ .reasoning-box .ms-markdown {
607
+ padding: 0 12px;
608
+ }
609
+
610
+ .reasoning-box::-webkit-scrollbar {
611
+ width: 6px;
612
+ }
613
+
614
+ .reasoning-box::-webkit-scrollbar-track {
615
+ background: #f1f1f1;
616
+ border-radius: 3px;
617
+ }
618
+
619
+ .reasoning-box::-webkit-scrollbar-thumb {
620
+ background: #888;
621
+ border-radius: 3px;
622
+ }
623
+
624
+ .reasoning-box::-webkit-scrollbar-thumb:hover {
625
+ background: #555;
626
+ }
627
+
628
+ .markdown-container {
629
+ height: 300px;
630
+ overflow-y: auto;
631
+ background-color: #f5f5f5;
632
+ border-radius: 4px;
633
+ margin-bottom: 12px;
634
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
635
+ font-size: 14px;
636
+ line-height: 1.6;
637
+ white-space: pre-wrap;
638
+ word-break: break-word;
639
+ width: 100%;
640
+ box-sizing: border-box;
641
+ scroll-behavior: smooth;
642
+ }
643
+
644
+ /* Example card styles */
645
+ .example-card {
646
+ flex: 1 1 calc(50% - 20px);
647
+ max-width: calc(50% - 20px);
648
+ margin: 6px;
649
+ transition: all 0.3s;
650
+ cursor: pointer;
651
+ border: 1px solid #e8e8e8;
652
+ border-radius: 8px;
653
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
654
+ }
655
+
656
+ .example-card:hover {
657
+ transform: translateY(-4px);
658
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
659
+ border-color: #d9d9d9;
660
+ }
661
+
662
+ .example-card .ant-card-meta-title {
663
+ font-size: 16px;
664
+ font-weight: 500;
665
+ margin-bottom: 8px;
666
+ color: #262626;
667
+ }
668
+
669
+ .example-card .ant-card-meta-description {
670
+ color: #666;
671
+ font-size: 14px;
672
+ line-height: 1.5;
673
+ }
674
+
675
+ /* Example tabs styles */
676
+ .example-tabs .ant-tabs-nav {
677
+ margin-bottom: 16px;
678
+ }
679
+
680
+ .example-tabs .ant-tabs-tab {
681
+ padding: 8px 16px;
682
+ font-size: 15px;
683
+ }
684
+
685
+ .example-tabs .ant-tabs-tab-active {
686
+ font-weight: 500;
687
+ }
688
+
689
+ /* Empty state styles */
690
+ .right_content {
691
+ display: flex;
692
+ flex-direction: column;
693
+ align-items: center;
694
+ justify-content: center;
695
+ width: 100%;
696
+ min-height: 620px;
697
+ background: #fff;
698
+ border-radius: 8px;
699
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
700
+ }
701
+
702
+ /* Add styles for the example cards container */
703
+ .example-tabs .ant-tabs-content {
704
+ padding: 0 8px;
705
+ }
706
+
707
+ .example-tabs .ant-flex {
708
+ margin: 0 -8px;
709
+ width: calc(100% + 16px);
710
+ }
711
+ """
712
+
713
+ def scroll_to_bottom():
714
+ return """
715
+ function() {
716
+ setTimeout(() => {
717
+ const reasoningBox = document.querySelector('.reasoning-box');
718
+ if (reasoningBox) {
719
+ reasoningBox.scrollTop = reasoningBox.scrollHeight;
720
+ }
721
+ const markdownContainer = document.querySelector('.markdown-container');
722
+ if (markdownContainer) {
723
+ markdownContainer.scrollTop = markdownContainer.scrollHeight;
724
+ }
725
+ }, 100);
726
+ }
727
+ """
728
+
729
+ with gr.Blocks(css=css) as demo, ms.Application(), antdx.XProvider():
730
+ with antd.Tabs() as tabs:
731
+ with antd.Tabs.Item(key="chat", label="Chatbot"):
732
+ with antd.Flex(vertical=True, gap="middle"):
733
+ chatbot = pro.Chatbot(
734
+ height="calc(100vh - 200px)",
735
+ markdown_config=ChatbotMarkdownConfig(allow_tags=["think"]),
736
+ welcome_config=ChatbotWelcomeConfig(
737
+ variant="borderless",
738
+ icon="./assets/minimax-logo.png",
739
+ title="Hello, I'm MiniMax-M1",
740
+ description="You can input text to get started.",
741
+ prompts=ChatbotPromptsConfig(
742
+ title="How can I help you today?",
743
+ styles={
744
+ "list": {
745
+ "width": '100%',
746
+ },
747
+ "item": {
748
+ "flex": 1,
749
+ },
750
+ },
751
+ items=DEFAULT_PROMPTS
752
+ )
753
+ ),
754
+ user_config=ChatbotUserConfig(actions=["copy", "edit"]),
755
+ bot_config=ChatbotBotConfig(
756
+ header=MODEL_NAME,
757
+ avatar="./assets/minimax-logo.png",
758
+ actions=["copy", "retry"]
759
+ )
760
+ )
761
+
762
+ with antdx.Sender() as sender:
763
+ with ms.Slot("prefix"):
764
+ with antd.Button(value=None, color="default", variant="text") as clear_btn:
765
+ with ms.Slot("icon"):
766
+ antd.Icon("ClearOutlined")
767
+
768
+ clear_btn.click(fn=clear, outputs=[chatbot])
769
+ submit_event = sender.submit(
770
+ fn=submit,
771
+ inputs=[sender, chatbot],
772
+ outputs=[sender, chatbot, clear_btn]
773
+ )
774
+ sender.cancel(
775
+ fn=cancel,
776
+ inputs=[chatbot],
777
+ outputs=[chatbot, sender, clear_btn],
778
+ cancels=[submit_event],
779
+ queue=False
780
+ )
781
+ chatbot.retry(
782
+ fn=retry,
783
+ inputs=[chatbot],
784
+ outputs=[sender, chatbot, clear_btn]
785
+ )
786
+ chatbot.welcome_prompt_select(fn=prompt_select, outputs=[sender])
787
+
788
+ with antd.Tabs.Item(key="code", label="Code Playground (WebDev)"):
789
+ with antd.Row(gutter=[32, 12], elem_classes="code-playground-container"):
790
+ with antd.Col(span=12):
791
+ with antd.Flex(vertical=True, gap="middle"):
792
+ code_input = antd.InputTextarea(
793
+ size="large",
794
+ allow_clear=True,
795
+ placeholder="Please enter what kind of application you want or choose an example below and click the button"
796
+ )
797
+ code_btn = antd.Button("Generate Code", type="primary", size="large")
798
+ with antd.Tabs(active_key="reasoning", visible=False) as output_tabs:
799
+ with antd.Tabs.Item(key="reasoning", label="🤔 Thinking Process"):
800
+ reasoning_output = legacy.Markdown(
801
+ elem_classes="reasoning-box"
802
+ )
803
+ with antd.Tabs.Item(key="code", label="💻 Generated Code"):
804
+ code_output = legacy.Markdown(elem_classes="markdown-container")
805
+
806
+ antd.Divider("Examples")
807
+
808
+ # Examples with categories
809
+ with antd.Tabs(elem_classes="example-tabs") as example_tabs:
810
+ for category, examples in EXAMPLES.items():
811
+ with antd.Tabs.Item(key=category, label=category):
812
+ with antd.Flex(gap="small", wrap=True):
813
+ for example in examples:
814
+ with antd.Card(
815
+ elem_classes="example-card",
816
+ hoverable=True
817
+ ) as example_card:
818
+ antd.Card.Meta(
819
+ title=example['title'],
820
+ description=example['description']
821
+ )
822
+
823
+ example_card.click(
824
+ fn=select_example,
825
+ inputs=[gr.State(example)],
826
+ outputs=[code_input]
827
+ )
828
+
829
+ with antd.Col(span=12):
830
+ with ms.Div(elem_classes="right_panel"):
831
+ gr.HTML('''
832
+ <div class="render_header">
833
+ <span class="header_btn"></span>
834
+ <span class="header_btn"></span>
835
+ <span class="header_btn"></span>
836
+ </div>
837
+ ''')
838
+
839
+ with antd.Tabs(active_key="empty", render_tab_bar="() => null") as state_tab:
840
+ with antd.Tabs.Item(key="empty"):
841
+ empty = antd.Empty(description="empty input", elem_classes="right_content")
842
+ with antd.Tabs.Item(key="loading"):
843
+ loading = antd.Spin(True, tip="Thinking and coding...", size="large", elem_classes="output-loading")
844
+ with antd.Tabs.Item(key="render"):
845
+ sandbox = gr.HTML(elem_classes="output-html")
846
+
847
+ code_btn.click(
848
+ generate_code,
849
+ inputs=[code_input],
850
+ outputs=[
851
+ code_output,
852
+ reasoning_output,
853
+ sandbox,
854
+ state_tab,
855
+ output_tabs,
856
+ loading
857
+ ]
858
+ )
859
+
860
+ # Add auto-scroll functionality
861
+ reasoning_output.change(
862
+ fn=scroll_to_bottom,
863
+ inputs=[],
864
+ outputs=[],
865
+ )
866
+ code_output.change(
867
+ fn=scroll_to_bottom,
868
+ inputs=[],
869
+ outputs=[],
870
+ )
871
+
872
+ def on_tab_change(tab_key):
873
+ return gr.update(active_key=tab_key, visible=True)
874
+
875
+ output_tabs.change(
876
+ fn=on_tab_change,
877
+ inputs=[output_tabs],
878
+ outputs=[output_tabs],
879
+ )
880
+
881
+ if __name__ == '__main__':
882
+ demo.queue(default_concurrency_limit=50).launch(ssr_mode=False)
assets/minimax-logo.png ADDED
config.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ DEFAULT_PROMPTS = [
2
+ {
3
+ "label": "🤔 Logical Reasoning",
4
+ "children": [
5
+ {
6
+ "description": "A cat is lying on a blanket because it is warm. What does 'it' refer to in this sentence?"
7
+ },
8
+ {
9
+ "description": "Snowflake : white'' - Which of the following groups of words has a relationship most similar to the relationship in the above word group? [ ''Doctor : learned'', ''Woman : gentle'', ''River : flowing'', ''Peony : wealth'' ]"
10
+ }
11
+ ]
12
+ },
13
+ {
14
+ "label": "📚 Knowledge Q&A",
15
+ "children": [
16
+ {
17
+ "description": "Why are most manhole covers round?"
18
+ },
19
+ {
20
+ "description": "What is the shortest distance between the United States and Russia?"
21
+ }
22
+ ]
23
+ }
24
+ ]
25
+
26
+ DEFAULT_CODE = {
27
+ "./index.html": """
28
+ <!DOCTYPE html>
29
+ <html lang="en">
30
+ <head>
31
+ <meta charset="UTF-8">
32
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
33
+ </head>
34
+ <body>
35
+ <h1>Hello, World!</h1>
36
+ </body>
37
+ </html>
38
+ """
39
+ }
40
+
41
+ SystemPrompt = """
42
+ You are a web development engineer, writing web pages according to the instructions below. You are a powerful code editing assistant capable of writing code and creating artifacts in conversations with users, or modifying and updating existing artifacts as requested by users.
43
+ All code is written in a single code block to form a complete code file for display, without separating HTML and JavaScript code. An artifact refers to a runnable complete code snippet, you prefer to integrate and output such complete runnable code rather than breaking it down into several code blocks. For certain types of code, they can render graphical interfaces in a UI window. After generation, please check the code execution again to ensure there are no errors in the output.
44
+ Output only the HTML, without any additional descriptive text. Make the UI looks modern and beautiful.
45
+ """
46
+
47
+ EXAMPLES = {
48
+ "UI Components": [
49
+ {
50
+ "title": "🔄 3D Flipping Card",
51
+ "description": "Create a 3D flipping card component that flips on hover. Include front and back content. Use only HTML and CSS (no JS). Make the flip smooth and realistic."
52
+ },
53
+ {
54
+ "title": "⚡ SVG Loading Animation",
55
+ "description": "Design an SVG dynamic loading animation with abstract style (e.g., rotating spinner, pulse dots) for web page loading status indication"
56
+ },
57
+ {
58
+ "title": "✨ Particle Background",
59
+ "description": "Create an HTML page with a canvas-based animated particle background. The particles should move smoothly and connect when close. Add a central heading text over the canvas"
60
+ },
61
+ {
62
+ "title": "🌈 Colorful Gradient Background",
63
+ "description": "Create an HTML page with a gradient background that transitions smoothly between multiple colors. Add a central heading text over the gradient"
64
+ }
65
+ ],
66
+ "Interactive Apps": [
67
+ {
68
+ "title": "📋 TODO list",
69
+ "description": "Create a modern, interactive to-do list web app. It should allow users to add and remove items. Use beautiful styling and animations."
70
+ },
71
+ {
72
+ "title": "📝 Sticky Note Wall",
73
+ "description": "Create a sticky note wall where users can create, edit, drag, and delete colorful sticky notes. Make it visually appealing"
74
+ },
75
+ {
76
+ "title": "⌨️ Typing Speed Game",
77
+ "description": "Build a typing speed test web app. Randomly show a sentence, and track the user's typing speed in WPM (words per minute). Provide live feedback with colors and accuracy. Make it visually appealing."
78
+ },
79
+ {
80
+ "title": "💰 Personal Finance Tracker",
81
+ "description": "Create a personal finance tracker with user input, and visualize income and expenses using animated pie and bar charts. Use modern, responsive design in a single HTML file."
82
+ }
83
+ ],
84
+ "Tools & Editors": [
85
+ {
86
+ "title": "📝 Markdown Editor with Preview",
87
+ "description": "Develop a modern markdown editor with real-time preview. Use a split pane layout for input and output, support bold, italics, headers, lists, links, images, etc. No external libraries."
88
+ },
89
+ {
90
+ "title": "🖼️ Image Filter Tool",
91
+ "description": "Create an image filter tool where users can upload an image and apply filters like grayscale, blur, brightness, contrast, etc. Show real-time preview."
92
+ }
93
+ ],
94
+ "Games & Visualizations": [
95
+ {
96
+ "title": "🧩 Maze Generator and Pathfinding Visualizer",
97
+ "description": "Create a maze generator and pathfinding visualizer. Randomly generate a maze and visualize A* algorithm solving it step by step. Use canvas and animations. Make it visually appealing."
98
+ },
99
+ {
100
+ "title": "💥 Particle Explosion Effect",
101
+ "description": "Implement a particle explosion effect when the user clicks anywhere on the page. Use canvas and JavaScript to animate colorful particles that fade out."
102
+ }
103
+ ]
104
+ }
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio==5.27.0
2
+ gradio-client>=0.30.0
3
+ requests
4
+ modelscope_studio==1.4.0
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