feat: add Xiaomi MiMo provider support#2240
Conversation
There was a problem hiding this comment.
Code Review
This pull request adds support for the Xiaomi MiMo Token Plan provider, integrating its models (mimo-v2.5-pro, mimo-v2.5, and mimo-v2-flash) into the configuration, CLI, and interactive TUI. The review feedback highlights three key issues: a regression in the model picker that removes the "auto" option for other pass-through providers, an issue where switching to Xiaomi with a "Max" reasoning effort silently disables thinking, and an overly restrictive model name normalization bypass that prevents the use of "auto" or custom models.
| let provider_models: &'static [(&'static str, &'static str)] = match app.api_provider { | ||
| crate::config::ApiProvider::Xiaomi => PICKER_MODELS_XIAOMI, | ||
| p if crate::config::provider_passes_model_through(p) => &[], | ||
| _ => PICKER_MODELS, | ||
| }; |
There was a problem hiding this comment.
For other pass-through providers, setting provider_models to &[] removes the "auto" ("select per turn") option from the model picker, which is a regression from the previous behavior where vec!["auto"] was shown. We should return &[("auto", "select per turn")] for other pass-through providers to preserve this option.
| let provider_models: &'static [(&'static str, &'static str)] = match app.api_provider { | |
| crate::config::ApiProvider::Xiaomi => PICKER_MODELS_XIAOMI, | |
| p if crate::config::provider_passes_model_through(p) => &[], | |
| _ => PICKER_MODELS, | |
| }; | |
| let provider_models: &'static [(&'static str, &'static str)] = match app.api_provider { | |
| crate::config::ApiProvider::Xiaomi => PICKER_MODELS_XIAOMI, | |
| p if crate::config::provider_passes_model_through(p) => &[("auto", "select per turn")], | |
| _ => PICKER_MODELS, | |
| }; |
| let selected_effort_idx = provider_efforts | ||
| .iter() | ||
| .position(|e| *e == normalized) | ||
| .unwrap_or(2); // default to High if somehow unknown | ||
| .unwrap_or(0); // default to first effort if unknown |
There was a problem hiding this comment.
If the user has ReasoningEffort::Max selected (which is common for DeepSeek), switching to Xiaomi MiMo will result in normalized being Max. Since PICKER_EFFORTS_MIMO does not contain Max, the position search will fail and default to 0 (ReasoningEffort::Off), silently disabling thinking. We should map ReasoningEffort::Max to ReasoningEffort::High when is_xiaomi is true.
let selected_effort_idx = provider_efforts
.iter()
.position(|e| {
if is_xiaomi && normalized == ReasoningEffort::Max {
*e == ReasoningEffort::High
} else {
*e == normalized
}
})
.unwrap_or(0);| if matches!(provider, ApiProvider::Xiaomi) && trimmed.to_ascii_lowercase().starts_with("mimo") { | ||
| if trimmed | ||
| .chars() | ||
| .all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '-' | '_' | '.' | ':')) | ||
| { | ||
| return Some(trimmed.to_string()); | ||
| } | ||
| return None; | ||
| } |
There was a problem hiding this comment.
Restricting the bypass to only models starting with "mimo" prevents the use of "auto" (the special TUI-level model name for auto-selection) or any custom/fine-tuned models that do not follow the "mimo" prefix. Since Xiaomi is a pass-through provider, we should allow any valid model name format.
if matches!(provider, ApiProvider::Xiaomi) {
if trimmed
.chars()
.all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '-' | '_' | '.' | ':'))
{
return Some(trimmed.to_string());
}
return None;
}- New ProviderKind::Xiaomi across config, TUI, CLI, and agent crates - Default endpoint: https://token-plan-cn.xiaomimimo.com/v1 - Models: mimo-v2.5-pro, mimo-v2.5, mimo-v2-flash - MiMo-specific thinking toggle (enabled/disabled) - /models command for Xiaomi provider - Provider-specific model picker UI Co-Authored-By: Hu Qiantao <huqiantao@HudeMacBook-Air.local>
Add first-class support for the Xiaomi MiMo API platform: - New provider variant `Xiaomi` across config, TUI, CLI, and agent crates - Default endpoint: https://api.xiaomimimo.com/v1 - Supports reasoning/thinking via reasoning_content field (same as DeepSeek) - Model registry entries for mimo-v2.5-pro and mimo-v2-flash - Provider picker, UI display names, and capability detection - Environment variable: MIMO_API_KEY - Example configuration in config.example.toml and .env.example Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Remove unused DEFAULT_XIAOMI_FLASH_MODEL constant from config/src/lib.rs - Add missing xiaomi field to ProvidersConfig struct and merge function - Update picker_lists_all_providers test to include Xiaomi MiMo Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Change default base_url to token-plan-cn.xiaomimimo.com/v1 (Token Plan API, not pay-as-you-go) - Add mimo-v2.5 (omni) model to registry with aliases - Fix provider_capability: mimo-v2.5 gets 1M/128K context (not 256K) Logic now checks for "flash" instead of "pro" to correctly classify - Update config examples with token-plan URLs, tp-xxxxx key format, and all three model options (pro/omni/flash) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When switching from DeepSeek to Xiaomi, the global default_text_model
("deepseek-v4-pro") was being inherited because Xiaomi is a pass-through
provider. Add dominated_model_prefix() guard to skip default_text_model
when it doesn't belong to the target provider's model namespace.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The dominated_model_prefix guard broke the existing multi-provider switching behavior where default_text_model is inherited across providers. Users can set provider-specific models via the [providers.xiaomi] config section instead. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…toggle
- Add provider-specific model list in /model picker (mimo-v2.5-pro, mimo-v2.5)
- Add provider-specific thinking effort (off/high only, MiMo doesn't support auto/max)
- Fix /models command to work with Xiaomi provider (fetches from /v1/models)
- Fix model validation to accept MiMo model names (not just DeepSeek)
- Fix thinking:off to send { type: "disabled" } instead of no-op for Xiaomi
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Restore "auto" option for pass-through providers (OpenAI, Moonshot, etc.) that was accidentally removed in the model picker refactor - Add mimo-v2-flash to PICKER_MODELS_XIAOMI so users can select it from /model - Add mimo-v2.5 to model_completion_names_for_provider for tab-completion - Map ReasoningEffort::Max → High when switching to Xiaomi (MiMo only supports on/off, so Max should map to High, not silently fall back to Off) - Broaden Xiaomi model name validation to accept "auto" and custom models, not just "mimo"-prefixed names Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Remove all mimo-v2-flash references: model registry entry, picker list, completion names, env/config examples, and the unused DEFAULT_XIAOMI_FLASH_MODEL constant. Only mimo-v2.5-pro and mimo-v2.5 are supported. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
580e90d to
af98fc1
Compare
Adds MiMo provider usage examples, env var references, and capability metadata across all three README languages, the provider registry, and the configuration reference. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Independent review: Comprehensive provider wiring across all 9 layers (config, secrets, CLI, TUI engine, picker, client, chat replay, agent registry, docs). Match arms are exhaustive — Rust compile-time enforcement will catch any future regressions. The MiMo-specific picker carve-out ( Concerns:
v0.8.48 (#2256) compatibility: No conflict — #2256 is unrelated (verification-gate work), does not add Xiaomi. Earlier intel was incorrect. |
|
Correction to my earlier comment: I miscalled v0.8.48's Xiaomi state. Verified just now via So v0.8.48 (#2256) DOES add Xiaomi MiMo independently. That means three implementations of the same provider are now in flight:
This PR's base URL aligns with v0.8.48's choice. PR #2246 disagrees. Whichever lands first wins the URL choice; the other two need to either align or document why a different endpoint is needed (region? auth shape?). v0.8.48 (#2256) relationship: direct conflict — both add |
|
Xiaomi MiMo cluster coordination — three implementations in flight:
Your enum name + base URL choice already aligns with v0.8.48's independent implementation, which is a strong signal for this naming. The gap to close vs #2246: tests. #2246 ships Recommendation: pull #2246's test scaffolding here (or have it land after #2256, then have this PR rebase + add tests as a follow-up). The CN URL choice is right for the CN-region access pattern that motivated MiMo in the first place; #2246's public URL is the international fallback. Your wiring is solid. |
- Fix misleading comment: MiMo uses thinking object like DeepSeek but does NOT send reasoning_effort (DeepSeek sends both) - Add 5 unit tests covering Xiaomi reasoning effort branches: disabled, high, low, noop, and DeepSeek comparison Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary
https://token-plan-cn.xiaomimimo.com/v1)mimo-v2.5-pro(reasoning flagship) andmimo-v2.5(omni)thinking: { type: "enabled"/"disabled" }) instead of DeepSeek-style reasoning_effort/modelscommand for Xiaomi provider to fetch available models from the APIChanges
Provider registration:
crates/config/src/lib.rs—ProviderKind::Xiaomivariant, default model/base_url constants, parse/as_strcrates/tui/src/config.rs—ApiProvider::Xiaomivariant, provider_capability (thinking enabled), all match armscrates/cli/src/lib.rs—ProviderArg::Xiaomi, provider list, env var mappingModel picker & validation:
crates/tui/src/tui/model_picker.rs— Provider-specific model lists (PICKER_MODELS_XIAOMI) and thinking effort lists (PICKER_EFFORTS_MIMOwith only off/high)crates/tui/src/config.rs—normalize_model_name_for_provideraccepts MiMo model names for Xiaomi providerAPI integration:
crates/tui/src/client.rs—apply_reasoning_effortsendsthinking: { type: "disabled" }for off,thinking: { type: "enabled" }for high/maxcrates/tui/src/client/chat.rs—requires_reasoning_content()includesmimoprefix for reasoning token replaycrates/tui/src/tui/ui.rs— Allow/modelscommand for Xiaomi providerModel registry:
crates/agent/src/lib.rs—ModelInfoentries formimo-v2.5-proandmimo-v2.5with aliasesConfig & docs:
config.example.toml—[providers.xiaomi]section with token-plan URL and model examples.env.example—MIMO_API_KEY,MIMO_BASE_URL,MIMO_MODELdocumentationTest plan
cargo fmt --all -- --checkpassescargo clippy --workspace --all-targets --all-features --locked -- -D warningspassescargo test --workspace --all-features --lockedpasses (4 pre-existing failures unrelated to this change)/provider→ Xiaomi MiMo,/model→ mimo-v2.5-pro, send a message/model→ switch thinking effort off/high, verify API accepts the request/models→ verify model list is fetched from Xiaomi APIGreptile Summary
This PR adds first-class Xiaomi MiMo provider support, wiring
ProviderKind::Xiaomi/ApiProvider::Xiaomithrough the full stack: config/secrets/CLI registration, TUI provider and model pickers,apply_reasoning_effort(MiMo-specificthinking: {type: "enabled"/"disabled"}toggle), reasoning-content replay for multi-turn tool calls, and allmatchexhaustiveness sites.mimo-v2.5-proandmimo-v2.5to the model registry with aliases and full thinking support; picker shows a dedicated three-row model list (auto / pro / omni) and a two-row effort list (off / high).MIMO_API_KEY,MIMO_BASE_URL,MIMO_MODELenv vars,[providers.xiaomi]TOML section,codewhale auth set --provider xiaomi, and/modelscommand support for the Xiaomi endpoint.docs/PROVIDERS.mddocuments that the MiMo API usesmax_completion_tokensinstead ofmax_tokens, but both the non-streaming (create_message_chat) and streaming (handle_chat_completion_stream) request-body constructors inchat.rsalways emit\"max_tokens\"— no provider-specific field rename is implemented.Confidence Score: 4/5
The provider integration is structurally complete and correct; one API field name discrepancy between the documentation and the implementation should be resolved before merging.
The
docs/PROVIDERS.mdentry added in this same PR explicitly states that the MiMo API usesmax_completion_tokensinstead ofmax_tokens. Both the non-streaming and streaming request paths inchat.rsalways send"max_tokens"with no Xiaomi-specific branch to rename the field. If the MiMo API does not honormax_tokens, every API call will silently ignore the configured token limit and fall back to the server's default. All other integration points (thinking toggle, reasoning-content replay, model picker, config/secrets handling) look correct and well-tested.crates/tui/src/client/chat.rs (lines 83 and 153) — both the non-streaming and streaming request-body constructors need a Xiaomi-specific branch to emit
max_completion_tokensinstead ofmax_tokens.Important Files Changed
max_tokenseven though PROVIDERS.md documents MiMo usesmax_completion_tokens.apply_reasoning_effortarms for off/high/max, sendingthinking: { type: "disabled"/"enabled" }withoutreasoning_effort. Unit tests cover all four effort cases.Sequence Diagram
sequenceDiagram participant User participant TUI participant Config as Config/Picker participant Client as DeepSeekClient participant MiMo as Xiaomi MiMo API User->>TUI: /provider xiaomi TUI->>Config: ApiProvider::Xiaomi → provider_capability() Config-->>TUI: "1M ctx, 128K out, thinking_supported=true" User->>TUI: /model mimo-v2.5-pro (effort: high) TUI->>Config: normalize_model_name_for_provider(Xiaomi, mimo-v2.5-pro) Config-->>TUI: mimo-v2.5-pro (pass-through) User->>TUI: send message TUI->>Client: "MessageRequest{model, max_tokens, reasoning_effort=high}" Client->>Client: "apply_reasoning_effort → body[thinking] = {type:enabled}" Note over Client: body[max_tokens] sent — docs say max_completion_tokens Client->>MiMo: "POST /v1/chat/completions {model, max_tokens, thinking:{type:enabled}}" MiMo-->>Client: stream with reasoning_content deltas Client->>Client: should_replay_reasoning_content(mimo-v2.5-pro, high) → true Client-->>TUI: assistant message + reasoning tokens TUI-->>User: display responseComments Outside Diff (1)
crates/tui/src/client/chat.rs, line 83 (link)max_tokensfield not renamed for Xiaomi MiMoThe PR's own
docs/PROVIDERS.mdentry forxiaomiexplicitly documents that the MiMo API usesmax_completion_tokensinstead ofmax_tokens. However, the request body here (and the identical line in the streaming path at line 153) always sends"max_tokens". There is no provider-specific translation anywhere in the codebase.If the MiMo API ignores
max_tokens(as the documentation implies), every non-streaming and streaming request will silently discard the configured token limit and fall back to whatever server-side default Xiaomi applies. The same fix is needed at the corresponding line inhandle_chat_completion_stream.Reviews (5): Last reviewed commit: "fix(mimo): address review feedback on Xi..." | Re-trigger Greptile