seawolf2357 commited on
Commit
53e8864
·
verified ·
1 Parent(s): 687cbda

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -215
app.py CHANGED
@@ -6,7 +6,6 @@ import os
6
  # ============================================
7
  # MiniMax-M2.1 Streaming Chat
8
  # Dual Provider: Novita (HF) + MiniMax Official API
9
- # Gradio 6.0 Compatible
10
  # ============================================
11
 
12
  # Provider 상태 관리
@@ -29,9 +28,9 @@ class ProviderManager:
29
 
30
  def get_status(self):
31
  if self.current_provider == "novita":
32
- return "🟢 Novita (HuggingFace)"
33
  else:
34
- return "🟡 MiniMax Official API"
35
 
36
  def switch_to_minimax(self):
37
  self.current_provider = "minimax"
@@ -76,94 +75,64 @@ def chat_with_minimax(messages):
76
  if chunk.choices and chunk.choices[0].delta.content:
77
  yield chunk.choices[0].delta.content
78
 
79
- def chat_stream(message, history):
80
  """
81
  Streaming chat with automatic fallback
82
- Gradio 6.0 messages format compatible
83
  """
84
  if not message.strip():
85
- yield [], provider.get_status()
86
  return
87
 
88
- # Build API messages from Gradio history
89
  api_messages = [{
90
  "role": "system",
91
  "content": "You are MiniMax-M2.1, a helpful AI assistant built by MiniMax. You excel at coding, tool use, and complex reasoning tasks. Respond in the same language as the user."
92
  }]
93
 
94
- # Convert Gradio messages format to API format
95
- for msg in history:
96
- if isinstance(msg, dict):
97
- api_messages.append({
98
- "role": msg.get("role", "user"),
99
- "content": msg.get("content", "")
100
- })
 
 
101
 
102
  api_messages.append({"role": "user", "content": message})
103
 
104
  response_text = ""
105
 
106
- # 1 시도: Novita
107
  if provider.novita_available and provider.current_provider == "novita":
108
  try:
109
  for chunk in chat_with_novita(api_messages):
110
  response_text += chunk
111
- # Gradio 6.0 messages format
112
- new_history = history + [
113
- {"role": "user", "content": message},
114
- {"role": "assistant", "content": response_text}
115
- ]
116
- yield new_history, "🟢 Novita (HuggingFace)"
117
  return
118
  except Exception as e:
119
  error_msg = str(e).lower()
120
- if any(keyword in error_msg for keyword in [
121
- "rate limit", "quota", "exceeded", "insufficient",
122
- "credit", "balance", "limit", "429", "402", "payment"
123
- ]):
124
- print(f"⚠️ Novita error (switching to MiniMax): {e}")
125
  provider.switch_to_minimax()
126
  else:
127
- print(f"⚠️ Novita temporary error: {e}")
128
 
129
- # 2 시도: MiniMax Official API
130
  try:
131
  if not os.environ.get("MINIMAX_API_KEY"):
132
- error_history = history + [
133
- {"role": "user", "content": message},
134
- {"role": "assistant", "content": "❌ Error: MINIMAX_API_KEY not configured. Please add it to Secrets."}
135
- ]
136
- yield error_history, "🔴 No Provider"
137
  return
138
 
139
  for chunk in chat_with_minimax(api_messages):
140
  response_text += chunk
141
- new_history = history + [
142
- {"role": "user", "content": message},
143
- {"role": "assistant", "content": response_text}
144
- ]
145
- yield new_history, "🟡 MiniMax Official API"
146
  return
147
 
148
  except Exception as e:
149
- error_msg = str(e)
150
- if "rate limit" not in error_msg.lower():
151
  provider.reset_to_novita()
152
-
153
- error_history = history + [
154
- {"role": "user", "content": message},
155
- {"role": "assistant", "content": f"❌ Error: {error_msg}"}
156
- ]
157
- yield error_history, "🔴 Error"
158
-
159
- def reset_provider():
160
- """수동으로 Novita로 리셋"""
161
- provider.reset_to_novita()
162
- return "✅ Reset to Novita!"
163
-
164
- def clear_chat():
165
- """채팅 초기화"""
166
- return [], "", provider.get_status()
167
 
168
  # ============================================
169
  # 🎨 Comic Classic Theme CSS
@@ -187,24 +156,13 @@ footer, .footer, .gradio-footer, .built-with-gradio {
187
  display: none !important;
188
  }
189
 
190
- .header-text h1 {
191
  font-family: 'Bangers', cursive !important;
192
  color: #1F2937 !important;
193
- font-size: 3.5rem !important;
194
  text-align: center !important;
195
- margin-bottom: 0.5rem !important;
196
  text-shadow: 4px 4px 0px #FACC15, 6px 6px 0px #1F2937 !important;
197
  letter-spacing: 3px !important;
198
- -webkit-text-stroke: 2px #1F2937 !important;
199
- }
200
-
201
- .subtitle {
202
- text-align: center !important;
203
- font-family: 'Comic Neue', cursive !important;
204
- font-size: 1.2rem !important;
205
- color: #1F2937 !important;
206
- margin-bottom: 1.5rem !important;
207
- font-weight: 700 !important;
208
  }
209
 
210
  .chatbot {
@@ -230,44 +188,35 @@ textarea:focus, input[type="text"]:focus {
230
  outline: none !important;
231
  }
232
 
233
- .gr-button-primary, button.primary {
234
  background: #3B82F6 !important;
235
  border: 3px solid #1F2937 !important;
236
  border-radius: 8px !important;
237
  color: #FFFFFF !important;
238
  font-family: 'Bangers', cursive !important;
239
- font-size: 1.3rem !important;
240
  letter-spacing: 2px !important;
241
- padding: 14px 28px !important;
242
- box-shadow: 5px 5px 0px #1F2937 !important;
243
  text-shadow: 1px 1px 0px #1F2937 !important;
244
  }
245
 
246
- .gr-button-primary:hover, button.primary:hover {
247
  background: #2563EB !important;
248
  transform: translate(-2px, -2px) !important;
249
- box-shadow: 7px 7px 0px #1F2937 !important;
250
  }
251
 
252
- .gr-button-primary:active, button.primary:active {
253
- transform: translate(3px, 3px) !important;
254
  box-shadow: 2px 2px 0px #1F2937 !important;
255
  }
256
 
257
- .gr-button-secondary, button.secondary {
258
  background: #EF4444 !important;
259
- border: 3px solid #1F2937 !important;
260
- border-radius: 8px !important;
261
- color: #FFFFFF !important;
262
- font-family: 'Bangers', cursive !important;
263
- font-size: 1.1rem !important;
264
- box-shadow: 4px 4px 0px #1F2937 !important;
265
- text-shadow: 1px 1px 0px #1F2937 !important;
266
  }
267
 
268
- .gr-button-secondary:hover, button.secondary:hover {
269
  background: #DC2626 !important;
270
- transform: translate(-2px, -2px) !important;
271
  }
272
 
273
  .gr-panel, .gr-box, .block, .gr-group {
@@ -291,19 +240,7 @@ textarea:focus, input[type="text"]:focus {
291
  padding: 15px !important;
292
  color: white !important;
293
  box-shadow: 5px 5px 0px #1F2937 !important;
294
- margin-bottom: 20px !important;
295
- }
296
-
297
- .provider-status {
298
- background: #1F2937 !important;
299
- border: 3px solid #10B981 !important;
300
- border-radius: 8px !important;
301
- padding: 10px 15px !important;
302
- color: #10B981 !important;
303
- font-family: 'Courier New', monospace !important;
304
- font-weight: bold !important;
305
- text-align: center !important;
306
- box-shadow: 4px 4px 0px #10B981 !important;
307
  }
308
 
309
  pre, code {
@@ -320,128 +257,47 @@ pre, code {
320
  ::-webkit-scrollbar-thumb:hover { background: #EF4444; }
321
 
322
  @media (max-width: 768px) {
323
- .header-text h1 { font-size: 2.2rem !important; }
324
  }
325
  """
326
 
327
  # ============================================
328
- # Gradio Interface (6.0 Compatible)
329
  # ============================================
330
- with gr.Blocks(title="MiniMax-M2.1 Chat") as demo:
331
-
332
- # Inject CSS
333
- gr.HTML(f"<style>{css}</style>")
334
-
335
- # HOME Badge
336
- gr.HTML("""
337
- <div style="text-align: center; margin: 20px 0 10px 0;">
338
- <a href="https://www.humangen.ai" target="_blank" style="text-decoration: none;">
339
- <img src="https://img.shields.io/static/v1?label=🏠 HOME&message=HUMANGEN.AI&color=0000ff&labelColor=ffcc00&style=for-the-badge" alt="HOME">
340
- </a>
341
 
342
- </div>
343
- """)
344
-
345
- # Header
346
- gr.Markdown("""# 🤖 MINIMAX-M2.1 CHAT 💬""", elem_classes="header-text")
347
- gr.Markdown("""<p class="subtitle">⚡ Claude Sonnet 4.5 수준의 코딩 & 에이전트 성능! 230B 파라미터 오픈소스 모델 🚀</p>""")
348
-
349
- # Model Info
350
- gr.HTML("""
351
- <div class="model-info">
352
- <div style="display: flex; justify-content: space-around; flex-wrap: wrap; text-align: center;">
353
- <div><strong style="font-size: 1.5rem;">230B</strong><br><span style="font-size: 0.9rem;">Total Params</span></div>
354
- <div><strong style="font-size: 1.5rem;">10B</strong><br><span style="font-size: 0.9rem;">Active Params</span></div>
355
- <div><strong style="font-size: 1.5rem;">88.6</strong><br><span style="font-size: 0.9rem;">VIBE Score</span></div>
356
- <div><strong style="font-size: 1.5rem;">#1</strong><br><span style="font-size: 0.9rem;">Open Source</span></div>
357
- </div>
358
- </div>
359
- """)
360
-
361
- # Provider Status
362
- with gr.Row():
363
- provider_status = gr.Textbox(
364
- value="🟢 Novita (HuggingFace)",
365
- label="🔌 Current Provider",
366
- interactive=False,
367
- elem_classes="provider-status"
368
- )
369
- reset_btn = gr.Button("🔄 Reset", variant="secondary", scale=0)
370
-
371
- # Chat Interface - Gradio 6.0 messages format
372
- chatbot = gr.Chatbot(
373
- label="💬 Chat",
374
- height=450,
375
- show_label=False,
376
- type="messages", # Gradio 6.0 format
377
- elem_classes="chatbot"
378
- )
379
-
380
- with gr.Row():
381
- msg = gr.Textbox(
382
- label="",
383
- placeholder="메시지를 입력하세요... (코딩, 분석, 창작 등 무엇이든!)",
384
- scale=9,
385
- container=False
386
- )
387
- submit_btn = gr.Button("📤 SEND", variant="primary", scale=1)
388
-
389
- with gr.Row():
390
- clear_btn = gr.Button("🗑️ CLEAR CHAT", variant="secondary")
391
-
392
- # Examples
393
- with gr.Accordion("💡 Example Prompts", open=False):
394
- gr.Examples(
395
- examples=[
396
- "Python으로 퀵소트 알고리즘을 구현해줘",
397
- "React로 Todo 앱 컴포넌트를 만들어줘",
398
- "Docker와 Kubernetes의 차이점을 설명해줘",
399
- "REST API 설계 베스트 프랙티스를 알려줘",
400
- "FastAPI로 JWT 인증 구현해줘",
401
- ],
402
- inputs=msg
403
- )
404
-
405
- # Provider Info
406
- with gr.Accordion("🔌 Provider Information", open=False):
407
- gr.Markdown("""
408
- ### 듀얼 프로바이더 시스템
409
-
410
- **1차: Novita (HuggingFace)** - HF PRO 크레딧 사용
411
-
412
- **2차: MiniMax Official API** - 크레딧 소진 시 자동 전환
413
-
414
- ### 필요한 Secrets
415
- - `HF_TOKEN`: HuggingFace 토큰
416
- - `MINIMAX_API_KEY`: MiniMax API 키
417
 
418
- **MiniMax API:** [platform.minimax.io](https://platform.minimax.io)
419
- """)
420
-
421
- # Model capabilities
422
- with gr.Accordion("🎯 Model Capabilities", open=False):
423
- gr.Markdown("""
424
- - **🏆 SOTA 코딩** — SWE-bench에서 Claude Sonnet 4.5 능가
425
- - **🌍 다국어** Python, Rust, Java, Go, C++, TypeScript
426
- - **🤖 에이전트** — 복잡한 멀티스텝 태스크
427
- - **💡 추론** — Interleaved Thinking
428
- - **🔧 도구** 함수 호출 및 API 연동
429
- """)
430
-
431
- # Event handlers
432
- def respond(message, history):
433
- if not message.strip():
434
- yield history, "", provider.get_status()
435
- return
436
-
437
- for new_history, status in chat_stream(message, history):
438
- yield new_history, "", status
439
-
440
- # Connect events
441
- msg.submit(respond, [msg, chatbot], [chatbot, msg, provider_status])
442
- submit_btn.click(respond, [msg, chatbot], [chatbot, msg, provider_status])
443
- clear_btn.click(clear_chat, outputs=[chatbot, msg, provider_status])
444
- reset_btn.click(reset_provider, outputs=[provider_status])
445
 
446
  if __name__ == "__main__":
447
- demo.launch(ssr_mode=False)
 
6
  # ============================================
7
  # MiniMax-M2.1 Streaming Chat
8
  # Dual Provider: Novita (HF) + MiniMax Official API
 
9
  # ============================================
10
 
11
  # Provider 상태 관리
 
28
 
29
  def get_status(self):
30
  if self.current_provider == "novita":
31
+ return "🟢 Novita"
32
  else:
33
+ return "🟡 MiniMax"
34
 
35
  def switch_to_minimax(self):
36
  self.current_provider = "minimax"
 
75
  if chunk.choices and chunk.choices[0].delta.content:
76
  yield chunk.choices[0].delta.content
77
 
78
+ def respond(message, history):
79
  """
80
  Streaming chat with automatic fallback
 
81
  """
82
  if not message.strip():
83
+ yield ""
84
  return
85
 
86
+ # Build API messages
87
  api_messages = [{
88
  "role": "system",
89
  "content": "You are MiniMax-M2.1, a helpful AI assistant built by MiniMax. You excel at coding, tool use, and complex reasoning tasks. Respond in the same language as the user."
90
  }]
91
 
92
+ # Add history
93
+ for h in history:
94
+ if isinstance(h, dict):
95
+ api_messages.append({"role": h.get("role", "user"), "content": h.get("content", "")})
96
+ elif isinstance(h, (list, tuple)) and len(h) == 2:
97
+ if h[0]:
98
+ api_messages.append({"role": "user", "content": h[0]})
99
+ if h[1]:
100
+ api_messages.append({"role": "assistant", "content": h[1]})
101
 
102
  api_messages.append({"role": "user", "content": message})
103
 
104
  response_text = ""
105
 
106
+ # 1차: Novita
107
  if provider.novita_available and provider.current_provider == "novita":
108
  try:
109
  for chunk in chat_with_novita(api_messages):
110
  response_text += chunk
111
+ yield response_text
 
 
 
 
 
112
  return
113
  except Exception as e:
114
  error_msg = str(e).lower()
115
+ if any(kw in error_msg for kw in ["rate limit", "quota", "exceeded", "insufficient", "credit", "balance", "limit", "429", "402", "payment"]):
116
+ print(f"⚠️ Novita error: {e}")
 
 
 
117
  provider.switch_to_minimax()
118
  else:
119
+ print(f"⚠️ Novita error: {e}")
120
 
121
+ # 2차: MiniMax
122
  try:
123
  if not os.environ.get("MINIMAX_API_KEY"):
124
+ yield "❌ Error: MINIMAX_API_KEY not configured."
 
 
 
 
125
  return
126
 
127
  for chunk in chat_with_minimax(api_messages):
128
  response_text += chunk
129
+ yield response_text
 
 
 
 
130
  return
131
 
132
  except Exception as e:
133
+ if "rate limit" not in str(e).lower():
 
134
  provider.reset_to_novita()
135
+ yield f"❌ Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
  # ============================================
138
  # 🎨 Comic Classic Theme CSS
 
156
  display: none !important;
157
  }
158
 
159
+ h1 {
160
  font-family: 'Bangers', cursive !important;
161
  color: #1F2937 !important;
162
+ font-size: 3rem !important;
163
  text-align: center !important;
 
164
  text-shadow: 4px 4px 0px #FACC15, 6px 6px 0px #1F2937 !important;
165
  letter-spacing: 3px !important;
 
 
 
 
 
 
 
 
 
 
166
  }
167
 
168
  .chatbot {
 
188
  outline: none !important;
189
  }
190
 
191
+ button {
192
  background: #3B82F6 !important;
193
  border: 3px solid #1F2937 !important;
194
  border-radius: 8px !important;
195
  color: #FFFFFF !important;
196
  font-family: 'Bangers', cursive !important;
197
+ font-size: 1.1rem !important;
198
  letter-spacing: 2px !important;
199
+ box-shadow: 4px 4px 0px #1F2937 !important;
 
200
  text-shadow: 1px 1px 0px #1F2937 !important;
201
  }
202
 
203
+ button:hover {
204
  background: #2563EB !important;
205
  transform: translate(-2px, -2px) !important;
206
+ box-shadow: 6px 6px 0px #1F2937 !important;
207
  }
208
 
209
+ button:active {
210
+ transform: translate(2px, 2px) !important;
211
  box-shadow: 2px 2px 0px #1F2937 !important;
212
  }
213
 
214
+ .secondary {
215
  background: #EF4444 !important;
 
 
 
 
 
 
 
216
  }
217
 
218
+ .secondary:hover {
219
  background: #DC2626 !important;
 
220
  }
221
 
222
  .gr-panel, .gr-box, .block, .gr-group {
 
240
  padding: 15px !important;
241
  color: white !important;
242
  box-shadow: 5px 5px 0px #1F2937 !important;
243
+ margin: 20px 0 !important;
 
 
 
 
 
 
 
 
 
 
 
 
244
  }
245
 
246
  pre, code {
 
257
  ::-webkit-scrollbar-thumb:hover { background: #EF4444; }
258
 
259
  @media (max-width: 768px) {
260
+ h1 { font-size: 2rem !important; }
261
  }
262
  """
263
 
264
  # ============================================
265
+ # Gradio ChatInterface (Simple & Compatible)
266
  # ============================================
 
 
 
 
 
 
 
 
 
 
 
267
 
268
+ # Header HTML
269
+ header_html = """
270
+ <div style="text-align: center; margin: 10px 0;">
271
+ <a href="https://www.humangen.ai" target="_blank" style="text-decoration: none;">
272
+ <img src="https://img.shields.io/static/v1?label=🏠 HOME&message=HUMANGEN.AI&color=0000ff&labelColor=ffcc00&style=for-the-badge" alt="HOME">
273
+ </a>
274
+
275
+ </div>
276
+ <div class="model-info">
277
+ <div style="display: flex; justify-content: space-around; flex-wrap: wrap; text-align: center;">
278
+ <div><strong style="font-size: 1.5rem;">230B</strong><br><span style="font-size: 0.9rem;">Total Params</span></div>
279
+ <div><strong style="font-size: 1.5rem;">10B</strong><br><span style="font-size: 0.9rem;">Active Params</span></div>
280
+ <div><strong style="font-size: 1.5rem;">88.6</strong><br><span style="font-size: 0.9rem;">VIBE Score</span></div>
281
+ <div><strong style="font-size: 1.5rem;">#1</strong><br><span style="font-size: 0.9rem;">Open Source</span></div>
282
+ </div>
283
+ </div>
284
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
 
286
+ # Create ChatInterface
287
+ demo = gr.ChatInterface(
288
+ fn=respond,
289
+ title="🤖 MINIMAX-M2.1 CHAT 💬",
290
+ description=header_html,
291
+ examples=[
292
+ "Python으로 퀵소트 알고리즘을 구현해줘",
293
+ "React로 Todo 컴포넌트를 만들어줘",
294
+ "Docker와 Kubernetes의 차이점을 설명해줘",
295
+ "FastAPI로 JWT 인증 구현해줘",
296
+ "머신러닝 모델 배포 파이프라인을 설계해줘",
297
+ ],
298
+ css=css,
299
+ theme=gr.themes.Soft(),
300
+ )
 
 
 
 
 
 
 
 
 
 
 
 
301
 
302
  if __name__ == "__main__":
303
+ demo.launch()