feat: add Xiaomi MiMo provider#2246
Conversation
Adds native xiaomi-mimo provider configuration, auth/env aliases, model registry entries, TUI request handling, tests, and docs. Keeps credentials in existing provider-scoped config/env/keyring paths and uses placeholders only in docs.
There was a problem hiding this comment.
Code Review
This pull request adds comprehensive support for the Xiaomi MiMo provider (xiaomi-mimo) across the CLI, TUI, configuration, and documentation. This includes setting up its default models (mimo-v2.5-pro and mimo-v2.5), mapping environment variables, handling its specific reasoning effort payload parameters, and routing token limits via max_completion_tokens. Feedback on the changes includes ensuring safe JSON manipulation in apply_provider_token_limit to prevent potential panics, and simplifying domain checks in uses_max_completion_tokens by removing redundant lowercase normalization.
| if let Some(object) = body.as_object_mut() { | ||
| object.remove("max_tokens"); | ||
| } | ||
| body["max_completion_tokens"] = json!(max_tokens); |
There was a problem hiding this comment.
The apply_provider_token_limit function currently modifies the max_completion_tokens field on body using direct indexing (body["max_completion_tokens"] = ...) outside of the if let Some(object) = body.as_object_mut() block. If body is ever not a JSON object (or null), this direct indexing will panic. To ensure safety and idiomatic Rust, perform all modifications inside the as_object_mut() block.
| if let Some(object) = body.as_object_mut() { | |
| object.remove("max_tokens"); | |
| } | |
| body["max_completion_tokens"] = json!(max_tokens); | |
| if let Some(object) = body.as_object_mut() { | |
| object.remove("max_tokens"); | |
| object.insert("max_completion_tokens".to_string(), json!(max_tokens)); | |
| } |
| domain.eq_ignore_ascii_case("xiaomimimo.com") | ||
| || domain.to_ascii_lowercase().ends_with(".xiaomimimo.com") |
There was a problem hiding this comment.
The url crate's Url::domain() method returns an already normalized lowercase domain. Therefore, calling to_ascii_lowercase() and using eq_ignore_ascii_case is redundant and introduces unnecessary string allocations. We can simplify this check to a direct comparison.
| domain.eq_ignore_ascii_case("xiaomimimo.com") | |
| || domain.to_ascii_lowercase().ends_with(".xiaomimimo.com") | |
| domain == "xiaomimimo.com" || domain.ends_with(".xiaomimimo.com") |
| fn uses_max_completion_tokens(base_url: &str) -> bool { | ||
| let Ok(url) = reqwest::Url::parse(base_url) else { | ||
| return false; | ||
| }; | ||
| let Some(domain) = url.domain() else { | ||
| return false; | ||
| }; | ||
|
|
||
| domain.eq_ignore_ascii_case("xiaomimimo.com") | ||
| || domain.to_ascii_lowercase().ends_with(".xiaomimimo.com") | ||
| } |
There was a problem hiding this comment.
Domain-based MiMo detection breaks custom proxy URLs
uses_max_completion_tokens only returns true when the configured base URL's domain is exactly xiaomimimo.com or a subdomain. If a user sets MIMO_BASE_URL (or [vision_model] base_url) to a non-Xiaomi domain — a corporate proxy, regional gateway, or local mirror — the vision tool will silently send max_tokens instead of the max_completion_tokens field that MiMo requires. Meanwhile the main chat path correctly handles this via apply_provider_token_limit keyed on ApiProvider::XiaomiMimo, so the two paths diverge for custom-endpoint configurations.
|
Independent review (Devin): Solid contribution — the provider wiring is structurally correct (variant, TOML key, env vars, picker, reasoning-effort branches). A few concrete gaps vs the parallel implementation in v0.8.48: Base URL: #2246 defaults to max_completion_tokens: MiMo rejects provider_passes_model_through: Not set for this provider. Without it, the model name is subject to DeepSeek-specific normalization that will mangle MiMo model IDs at runtime. provider_accepts_reasoning_content: Not added. Required for the assistant-turn reasoning-content replay logic to work correctly with thinking-mode responses. No reasoning unit test. v0.8.48 adds v0.8.48 (#2256) compatibility: Direct conflict — both add a Xiaomi MiMo variant to Sister-PR (#2240) relationship: #2240 (HUQIANTAO) and #2246 (AdityaVG13) are functionally identical — same variant name, same base URL, same reasoning branches, no |
|
Xiaomi MiMo cluster coordination — three implementations in flight:
This PR's reasoning_effort branches + Cross-pinging @AdityaVG13 @HUQIANTAO — converging plan being figured out. Your enum-name + URL choice is the live merit question. |
Summary
Adds Xiaomi MiMo as a first-class CodeWhale provider.
This includes:
xiaomi-mimoModel And Capability Notes
The default Xiaomi MiMo chat model is:
mimo-v2.5-proThe static registry also includes:
mimo-v2.5MiMo-specific request behavior:
max_completion_tokensinstead ofmax_tokensthinkingfieldFor vision, this PR keeps CodeWhale's existing separation between the main chat provider and the
image_analyzetool. MiMo vision is documented through[vision_model]usingmimo-v2.5, which matches Xiaomi's current image-understanding docs.Related Context
I did not find an open upstream PR or issue requesting native Xiaomi MiMo provider support.
Related only: #1572 mentions MiMo model IDs in a custom-provider context, but does not add native CodeWhale provider support.
Testing
cargo fmt --all -- --checkgit diff --checkcargo check -p codewhale-tuicargo clippy -p codewhale-tui --all-targets --all-featurescargo test -p codewhale-agent xiaomi_mimo --libcargo test -p codewhale-config xiaomi_mimo --libcargo test -p codewhale-secrets xiaomi_mimo --libcargo test -p codewhale-cli build_tui_command_forwards_provider_keyring_env_vars_for_all_providers --libcargo test -p codewhale-tui xiaomi_mimocargo test -p codewhale-tui vision::tools::testscargo test -p codewhale-tui reasoning_effort_uses_xiaomi_mimo_thinking_parameter_onlycargo test -p codewhale-tui provider_capability_xiaomi_mimo_has_thinking_no_cacheHygiene
Notes
Full
cargo test --workspace --all-featureswas not used as the gating signal locally because it hits existing local/upstream-sensitive failures unrelated to Xiaomi MiMo. Focused provider and vision coverage is passing.Greptile Summary
This PR adds Xiaomi MiMo as a first-class provider across all layers of the codebase: config/secrets/CLI/TUI/agent registry. The integration follows the established pattern used by other providers like Wanjie Ark and OpenRouter.
ProviderKind::XiaomiMimo/ApiProvider::XiaomiMimoadded consistently across enum variants, serde aliases (mimo,xiaomi,xiaomi_mimo), env-var overrides (XIAOMI_MIMO_API_KEY/MIMO_API_KEY,MIMO_BASE_URL,MIMO_MODEL), config-key mappings, and the TUI provider picker.max_tokens→max_completion_tokensviaapply_provider_token_limit; reasoning maps to MiMo'sthinkingfield (enabled/disabled) without forwarding areasoning_effortvalue, applied correctly across all three effort-level branches inapply_reasoning_effort.request_payloadhelper centralises payload construction and uses a domain check on the configured base URL to selectmax_completion_tokensforxiaomimimo.comendpoints.Confidence Score: 4/5
Safe to merge for most users; the vision tool's token-field selection fails silently for custom MiMo proxy URLs, which matters only for users combining the
image_analyzetool with a non-xiaomimimo.com endpoint.The bulk of the change — config, CLI, secrets, TUI provider switching, reasoning-effort wiring, and chat-path token-field replacement — is well-structured and matches the established provider pattern exactly. The one concrete defect is in
vision/tools.rs:uses_max_completion_tokensrelies on a domain check rather than the provider enum, so any MiMo-compatible endpoint behind a proxy or custom hostname will receivemax_tokensinstead ofmax_completion_tokensin vision requests, which MiMo requires. The main chat path avoids this problem entirely by keying offApiProvider::XiaomiMimo.crates/tui/src/vision/tools.rs — the domain-heuristic token-field selection should be reviewed before any user-facing documentation encourages custom proxy setups for MiMo vision.
Important Files Changed
max_completion_tokensselection breaks for custom proxy URLs unlike the main chat path which uses an explicit provider enum checkapply_provider_token_limithelper that correctly swapsmax_tokens→max_completion_tokensfor XiaomiMimo; called in both streaming and non-streaming chat pathsthinkingfield set and noreasoning_effortkey; matching test coverage confirms behaviour["XIAOMI_MIMO_API_KEY", "MIMO_API_KEY"]; test coverage confirms alias resolutionmimo-v2.5-proas default withmimoalias,mimo-v2.5withxiaomi-mimo-v2.5alias); both with tools and reasoning enabledFlowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[User request\nApiProvider::XiaomiMimo] --> B{Chat or Vision?} B --> C[Chat path\nclient/chat.rs] C --> C1[Build body with\nmax_tokens] C1 --> C2[apply_provider_token_limit\nremoves max_tokens\nadds max_completion_tokens] C2 --> C3[apply_reasoning_effort\nsets thinking: enabled/disabled\nno reasoning_effort key] C3 --> C4[POST /chat/completions] B --> D[Vision path\nvision/tools.rs] D --> D1[request_payload] D1 --> D2{uses_max_completion_tokens?\ndomain == xiaomimimo.com?} D2 -->|Yes| D3[max_completion_tokens] D2 -->|No - custom proxy| D4[max_tokens ⚠️] D3 --> D5[POST /chat/completions] D4 --> D5Reviews (1): Last reviewed commit: "feat: add Xiaomi MiMo provider" | Re-trigger Greptile