Compare commits

...

2 Commits

Author SHA1 Message Date
Augustin
7d0f807fb0 feat(ai): add Xiaomi MiMo provider, ZAI as last-resort fallback
All checks were successful
Beta Release / beta (push) Successful in 57s
Add MiMo-V2.5-Pro from Xiaomi Token Plan as a new AI provider with
base URL https://token-plan-ams.xiaomimimo.com/v1. The /model change
command now switches between MiniMax and MiMo only. ZAI is always
placed last in the fallback chain as the provider of ultimate resort.
Config panel shows MiniMax and MiMo cards.

💘 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>
2026-04-24 21:22:34 +02:00
Augustin
cbf623b98b fix(terminal): use absolute positioning for content panels
All checks were successful
Beta Release / beta (push) Successful in 50s
height:100% on .content>div fails because .content uses flex:1
without explicit height. Switch to position:absolute;inset:0 which
correctly fills the content area and gives xterm proper container
dimensions for fitAddon.

💘 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>
2026-04-24 21:13:20 +02:00
6 changed files with 29 additions and 8 deletions

View File

@@ -530,6 +530,11 @@ func (s *Server) handleProvidersQuota(w http.ResponseWriter, r *http.Request) {
} }
} }
} }
case "mimo":
q.Healthy = p.APIKey != ""
if p.APIKey == "" {
q.Error = "no API key"
}
case "claude", "anthropic": case "claude", "anthropic":
// Claude Code n'a pas d'API externe, vérifier l'installation // Claude Code n'a pas d'API externe, vérifier l'installation
claudePath := "/usr/bin/claude" claudePath := "/usr/bin/claude"

View File

@@ -269,6 +269,12 @@ func Default() *MuyueConfig {
BaseURL: "https://api.minimax.io/v1", BaseURL: "https://api.minimax.io/v1",
Active: true, Active: true,
}, },
{
Name: "mimo",
Model: "MiMo-V2.5-Pro",
BaseURL: "https://token-plan-ams.xiaomimimo.com/v1",
Active: false,
},
{ {
Name: "zai", Name: "zai",
Model: "glm", Model: "glm",

View File

@@ -476,6 +476,8 @@ func getProviderBaseURL(name string) string {
return "https://api.openai.com/v1" return "https://api.openai.com/v1"
case "zai": case "zai":
return "https://api.z.ai/v1" return "https://api.z.ai/v1"
case "mimo":
return "https://token-plan-ams.xiaomimimo.com/v1"
default: default:
return "" return ""
} }
@@ -503,11 +505,19 @@ func (o *Orchestrator) sendWithFallback(reqBody ChatRequest, baseURLOverride str
if o.provider != nil { if o.provider != nil {
providerOrder = append(providerOrder, o.provider) providerOrder = append(providerOrder, o.provider)
} }
var zaiProvider *config.AIProvider
for _, p := range providers { for _, p := range providers {
if o.provider == nil || p.Name != o.provider.Name { if o.provider == nil || p.Name != o.provider.Name {
providerOrder = append(providerOrder, p) if p.Name == "zai" {
zaiProvider = p
} else {
providerOrder = append(providerOrder, p)
}
} }
} }
if zaiProvider != nil {
providerOrder = append(providerOrder, zaiProvider)
}
var lastErr error var lastErr error
var triedProviders []string var triedProviders []string

View File

@@ -343,7 +343,7 @@ function PanelProviders({ providers, editProvider, providerForm, setProviderForm
setValidating(null) setValidating(null)
} }
const displayed = providers.filter(p => p.name === 'minimax' || p.name === 'zai') const displayed = providers.filter(p => p.name === 'minimax' || p.name === 'mimo')
return ( return (
<div className="config-providers-list"> <div className="config-providers-list">

View File

@@ -452,15 +452,15 @@ export default function Studio({ api }) {
api.getProviders().then(data => { api.getProviders().then(data => {
const providers = data.providers || [] const providers = data.providers || []
const minimax = providers.find(p => p.name.toUpperCase() === 'MINIMAX') const minimax = providers.find(p => p.name.toUpperCase() === 'MINIMAX')
const zai = providers.find(p => p.name.toUpperCase() === 'ZAI') const mimo = providers.find(p => p.name.toUpperCase() === 'MIMO')
if (!minimax || !zai) { if (!minimax || !mimo) {
setMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: 'MiniMax et ZAI doivent être configurés pour utiliser `/model change`.', time: new Date().toISOString() }]) setMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: 'MiniMax et MiMo doivent être configurés pour utiliser `/model change`.', time: new Date().toISOString() }])
return return
} }
const active = providers.find(p => p.active) const active = providers.find(p => p.active)
const activeName = active ? active.name.toUpperCase() : '' const activeName = active ? active.name.toUpperCase() : ''
const switchTo = activeName === 'MINIMAX' ? 'ZAI' : 'MINIMAX' const switchTo = activeName === 'MINIMAX' ? 'MIMO' : 'MINIMAX'
const target = switchTo === 'MINIMAX' ? minimax : zai const target = switchTo === 'MINIMAX' ? minimax : mimo
api.saveProvider({ name: target.name, active: true }).then(() => { api.saveProvider({ name: target.name, active: true }).then(() => {
setMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: `✓ Provider changé: **${target.name}** (${target.model})`, time: new Date().toISOString() }]) setMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: `✓ Provider changé: **${target.name}** (${target.model})`, time: new Date().toISOString() }])
}).catch(() => { }).catch(() => {

View File

@@ -155,7 +155,7 @@ input::placeholder { color: var(--text-disabled); }
.header-clock { font-family: var(--font-mono); font-size: 12px; color: var(--accent); font-weight: 600; } .header-clock { font-family: var(--font-mono); font-size: 12px; color: var(--accent); font-weight: 600; }
.content { flex: 1; overflow: hidden; position: relative; } .content { flex: 1; overflow: hidden; position: relative; }
.content > div { height: 100%; } .content > div { position: absolute; inset: 0; overflow: hidden; }
.tab-hidden { display: none; } .tab-hidden { display: none; }
.statusbar { .statusbar {