MiniMax-M2.1 / app.py
seawolf2357's picture
Update app.py
a9ece2e verified
import gradio as gr
from huggingface_hub import InferenceClient
from openai import OpenAI
import os
# ============================================
# MiniMax-M2.1 Streaming Chat
# Dual Provider: Novita (HF) + MiniMax Official API
# Gradio 6.0 Compatible - Full Width Layout
# ============================================
# Provider Management
class ProviderManager:
def __init__(self):
self.current_provider = "novita"
self.novita_available = True
# Novita Client (via HuggingFace)
self.novita_client = InferenceClient(
provider="novita",
api_key=os.environ.get("HF_TOKEN"),
)
# MiniMax Official API Client
self.minimax_client = OpenAI(
api_key=os.environ.get("MINIMAX_API_KEY", ""),
base_url="https://api.minimax.chat/v1"
)
def get_status(self):
if self.current_provider == "novita":
return "[Active] Novita (HuggingFace)"
else:
return "[Active] MiniMax Official API"
def switch_to_minimax(self):
self.current_provider = "minimax"
self.novita_available = False
print("Switched to MiniMax Official API")
def reset_to_novita(self):
self.current_provider = "novita"
self.novita_available = True
print("Reset to Novita provider")
# Global provider manager
provider = ProviderManager()
def chat_with_novita(messages):
"""Streaming via Novita provider"""
stream = provider.novita_client.chat.completions.create(
model="MiniMaxAI/MiniMax-M2.1",
messages=messages,
max_tokens=4096,
temperature=1.0,
top_p=0.95,
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
yield chunk.choices[0].delta.content
def chat_with_minimax(messages):
"""Streaming via MiniMax Official API"""
stream = provider.minimax_client.chat.completions.create(
model="MiniMax-M2.1",
messages=messages,
max_tokens=4096,
temperature=1.0,
top_p=0.95,
stream=True
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
yield chunk.choices[0].delta.content
def chat_respond(message, history):
"""
Streaming chat with automatic fallback
Gradio 6.0 messages format: [{"role": "user/assistant", "content": "..."}]
"""
if not message.strip():
return history, provider.get_status()
# Build API messages
api_messages = [{
"role": "system",
"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."
}]
# Add history (Gradio 6.0 messages format)
for h in history:
if isinstance(h, dict):
api_messages.append({"role": h.get("role", "user"), "content": h.get("content", "")})
api_messages.append({"role": "user", "content": message})
response_text = ""
# 1st: Try Novita
if provider.novita_available and provider.current_provider == "novita":
try:
for chunk in chat_with_novita(api_messages):
response_text += chunk
new_history = history + [
{"role": "user", "content": message},
{"role": "assistant", "content": response_text}
]
yield new_history, "[Active] Novita (HuggingFace)"
return
except Exception as e:
error_msg = str(e).lower()
if any(kw in error_msg for kw in ["rate limit", "quota", "exceeded", "insufficient", "credit", "balance", "limit", "429", "402", "payment"]):
print(f"Novita error: {e}")
provider.switch_to_minimax()
else:
print(f"Novita error: {e}")
# 2nd: Fallback to MiniMax
try:
if not os.environ.get("MINIMAX_API_KEY"):
new_history = history + [
{"role": "user", "content": message},
{"role": "assistant", "content": "Error: MINIMAX_API_KEY not configured."}
]
yield new_history, "[Error] No API Key"
return
for chunk in chat_with_minimax(api_messages):
response_text += chunk
new_history = history + [
{"role": "user", "content": message},
{"role": "assistant", "content": response_text}
]
yield new_history, "[Active] MiniMax Official API"
return
except Exception as e:
if "rate limit" not in str(e).lower():
provider.reset_to_novita()
new_history = history + [
{"role": "user", "content": message},
{"role": "assistant", "content": f"Error: {str(e)}"}
]
yield new_history, "[Error]"
def reset_provider_fn():
"""Manually reset to Novita"""
provider.reset_to_novita()
return "Reset to Novita!"
def clear_chat_fn():
"""Clear chat history"""
return [], "", provider.get_status()
# ============================================
# Comic Classic Theme CSS
# ============================================
css = """
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@400;700&display=swap');
.gradio-container {
background-color: #FEF9C3 !important;
background-image: radial-gradient(#1F2937 1px, transparent 1px) !important;
background-size: 20px 20px !important;
min-height: 100vh !important;
font-family: 'Comic Neue', cursive, sans-serif !important;
}
.huggingface-space-header, #space-header, .space-header, [class*="space-header"] {
display: none !important;
}
footer, .footer, .gradio-footer, .built-with-gradio {
display: none !important;
}
.header-text h1 {
font-family: 'Bangers', cursive !important;
color: #1F2937 !important;
font-size: 3.5rem !important;
text-align: center !important;
text-shadow: 4px 4px 0px #FACC15, 6px 6px 0px #1F2937 !important;
letter-spacing: 3px !important;
-webkit-text-stroke: 2px #1F2937 !important;
}
.subtitle {
text-align: center !important;
font-family: 'Comic Neue', cursive !important;
font-size: 1.2rem !important;
color: #1F2937 !important;
font-weight: 700 !important;
}
.gr-panel, .gr-box, .block, .gr-group {
background: #FFFFFF !important;
border: 3px solid #1F2937 !important;
border-radius: 8px !important;
box-shadow: 6px 6px 0px #1F2937 !important;
}
textarea, input[type="text"] {
background: #FFFFFF !important;
border: 3px solid #1F2937 !important;
border-radius: 8px !important;
color: #1F2937 !important;
font-family: 'Comic Neue', cursive !important;
font-size: 1rem !important;
font-weight: 700 !important;
}
textarea:focus, input[type="text"]:focus {
border-color: #3B82F6 !important;
box-shadow: 4px 4px 0px #3B82F6 !important;
outline: none !important;
}
.gr-button-primary, button.primary {
background: #3B82F6 !important;
border: 3px solid #1F2937 !important;
border-radius: 8px !important;
color: #FFFFFF !important;
font-family: 'Bangers', cursive !important;
font-size: 1.3rem !important;
letter-spacing: 2px !important;
padding: 14px 28px !important;
box-shadow: 5px 5px 0px #1F2937 !important;
text-shadow: 1px 1px 0px #1F2937 !important;
}
.gr-button-primary:hover, button.primary:hover {
background: #2563EB !important;
transform: translate(-2px, -2px) !important;
box-shadow: 7px 7px 0px #1F2937 !important;
}
.gr-button-secondary, button.secondary {
background: #EF4444 !important;
border: 3px solid #1F2937 !important;
border-radius: 8px !important;
color: #FFFFFF !important;
font-family: 'Bangers', cursive !important;
font-size: 1.1rem !important;
box-shadow: 4px 4px 0px #1F2937 !important;
}
.gr-button-secondary:hover, button.secondary:hover {
background: #DC2626 !important;
transform: translate(-2px, -2px) !important;
}
.status-box textarea {
background: #1F2937 !important;
color: #10B981 !important;
font-family: 'Courier New', monospace !important;
border: 3px solid #10B981 !important;
box-shadow: 4px 4px 0px #10B981 !important;
}
.chatbot {
border: 3px solid #1F2937 !important;
border-radius: 12px !important;
box-shadow: 6px 6px 0px #1F2937 !important;
background: #FFFFFF !important;
}
pre, code {
background: #1F2937 !important;
color: #10B981 !important;
border: 2px solid #10B981 !important;
border-radius: 6px !important;
font-family: 'Courier New', monospace !important;
}
::-webkit-scrollbar { width: 12px; }
::-webkit-scrollbar-track { background: #FEF9C3; border: 2px solid #1F2937; }
::-webkit-scrollbar-thumb { background: #3B82F6; border: 2px solid #1F2937; }
::-webkit-scrollbar-thumb:hover { background: #EF4444; }
@media (max-width: 768px) {
.header-text h1 { font-size: 2.2rem !important; }
}
"""
# ============================================
# Gradio Interface - Full Width Layout (English, No Emoji)
# ============================================
with gr.Blocks(fill_height=True) as demo:
# Inject CSS via HTML
gr.HTML(f"<style>{css}</style>")
# HOME Badge
gr.HTML("""
<div style="text-align: center; margin: 20px 0 10px 0;">
<a href="https://www.humangen.ai" target="_blank" style="text-decoration: none;">
<img src="https://img.shields.io/static/v1?label=HOME&message=HUMANGEN.AI&color=0000ff&labelColor=ffcc00&style=for-the-badge" alt="HOME">
</a>
</div>
""")
# Header Title
gr.Markdown("""# MINIMAX-M2.1 CHAT""", elem_classes="header-text")
gr.Markdown("""<p class="subtitle">Claude Sonnet 4.5-level Coding & Agent Performance! 230B Parameter Open Source Model</p>""")
# Model Info Box
gr.HTML("""
<div style="background: linear-gradient(135deg, #3B82F6 0%, #8B5CF6 100%); border: 3px solid #1F2937; border-radius: 12px; padding: 15px; color: white; box-shadow: 5px 5px 0px #1F2937; margin: 0 auto 20px auto; max-width: 1200px;">
<div style="display: flex; justify-content: space-around; flex-wrap: wrap; text-align: center;">
<div><strong style="font-size: 1.5rem;">230B</strong><br><span style="font-size: 0.9rem;">Total Params</span></div>
<div><strong style="font-size: 1.5rem;">10B</strong><br><span style="font-size: 0.9rem;">Active Params</span></div>
<div><strong style="font-size: 1.5rem;">88.6</strong><br><span style="font-size: 0.9rem;">VIBE Score</span></div>
<div><strong style="font-size: 1.5rem;">#1</strong><br><span style="font-size: 0.9rem;">Open Source</span></div>
</div>
</div>
""")
# Provider Status Row
with gr.Row():
provider_status = gr.Textbox(
value="[Active] Novita (HuggingFace)",
label="Current Provider",
interactive=False,
elem_classes="status-box",
scale=4
)
reset_btn = gr.Button("Reset Provider", variant="secondary", scale=1)
# Chatbot - Full Width
chatbot = gr.Chatbot(
label="Chat",
height=500,
show_label=False,
elem_classes="chatbot"
)
# Input Row
with gr.Row():
msg = gr.Textbox(
label="",
placeholder="Enter your message... (coding, analysis, creative writing, anything!)",
scale=8,
container=False
)
submit_btn = gr.Button("SEND", variant="primary", scale=2)
# Clear Button
clear_btn = gr.Button("CLEAR CHAT", variant="secondary")
# Event Handlers
def respond(message, history):
if not message.strip():
yield history, provider.get_status()
return
for result in chat_respond(message, history):
yield result
# Connect events
msg.submit(respond, [msg, chatbot], [chatbot, provider_status]).then(
lambda: "", outputs=[msg]
)
submit_btn.click(respond, [msg, chatbot], [chatbot, provider_status]).then(
lambda: "", outputs=[msg]
)
clear_btn.click(clear_chat_fn, outputs=[chatbot, msg, provider_status])
reset_btn.click(reset_provider_fn, outputs=[provider_status])
if __name__ == "__main__":
demo.launch(ssr_mode=False)