Skip to content

feat: add Xiaomi MiMo provider#2246

Open
AdityaVG13 wants to merge 1 commit into
Hmbown:mainfrom
AdityaVG13:feat/add-xiaomi-mimo-provider
Open

feat: add Xiaomi MiMo provider#2246
AdityaVG13 wants to merge 1 commit into
Hmbown:mainfrom
AdityaVG13:feat/add-xiaomi-mimo-provider

Conversation

@AdityaVG13
Copy link
Copy Markdown

@AdityaVG13 AdityaVG13 commented May 27, 2026

Summary

Adds Xiaomi MiMo as a first-class CodeWhale provider.

This includes:

  • provider/config support for xiaomi-mimo
  • CLI forwarding and auth/keyring env propagation
  • TUI provider switching and provider picker support
  • MiMo request shaping for OpenAI-compatible chat completions
  • docs and examples across English, Chinese, and Japanese READMEs
  • focused tests for provider resolution, env aliases, request payloads, and TUI behavior

Model And Capability Notes

The default Xiaomi MiMo chat model is:

  • mimo-v2.5-pro

The static registry also includes:

  • mimo-v2.5

MiMo-specific request behavior:

  • uses max_completion_tokens instead of max_tokens
  • maps reasoning control to MiMo's thinking field
  • keeps tools enabled for the MiMo provider path
  • sets provider capability metadata to 1M context / 128K max output for the current V2.5 models

For vision, this PR keeps CodeWhale's existing separation between the main chat provider and the image_analyze tool. MiMo vision is documented through [vision_model] using mimo-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 -- --check
  • git diff --check
  • cargo check -p codewhale-tui
  • cargo clippy -p codewhale-tui --all-targets --all-features
  • cargo test -p codewhale-agent xiaomi_mimo --lib
  • cargo test -p codewhale-config xiaomi_mimo --lib
  • cargo test -p codewhale-secrets xiaomi_mimo --lib
  • cargo test -p codewhale-cli build_tui_command_forwards_provider_keyring_env_vars_for_all_providers --lib
  • cargo test -p codewhale-tui xiaomi_mimo
  • cargo test -p codewhale-tui vision::tools::tests
  • cargo test -p codewhale-tui reasoning_effort_uses_xiaomi_mimo_thinking_parameter_only
  • cargo test -p codewhale-tui provider_capability_xiaomi_mimo_has_thinking_no_cache

Hygiene

  • Added-line secret/local marker scan: no credible new secret or local-path leak found
  • Dotfile/dotdir path changes: none
  • Public hygiene scan was run in review-only mode. It reports inherited repo-wide baseline findings, but this PR diff has no credible new secret, personal path, or dotdir leak.

Notes

Full cargo test --workspace --all-features was 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.

  • Provider plumbing: ProviderKind::XiaomiMimo / ApiProvider::XiaomiMimo added 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.
  • Request shaping: Chat completions correctly swap max_tokensmax_completion_tokens via apply_provider_token_limit; reasoning maps to MiMo's thinking field (enabled/disabled) without forwarding a reasoning_effort value, applied correctly across all three effort-level branches in apply_reasoning_effort.
  • Vision tool: A new request_payload helper centralises payload construction and uses a domain check on the configured base URL to select max_completion_tokens for xiaomimimo.com endpoints.

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_analyze tool 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_tokens relies on a domain check rather than the provider enum, so any MiMo-compatible endpoint behind a proxy or custom hostname will receive max_tokens instead of max_completion_tokens in vision requests, which MiMo requires. The main chat path avoids this problem entirely by keying off ApiProvider::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

Filename Overview
crates/tui/src/vision/tools.rs Adds MiMo-aware request payload builder; domain-based heuristic for max_completion_tokens selection breaks for custom proxy URLs unlike the main chat path which uses an explicit provider enum check
crates/tui/src/client/chat.rs Adds apply_provider_token_limit helper that correctly swaps max_tokensmax_completion_tokens for XiaomiMimo; called in both streaming and non-streaming chat paths
crates/tui/src/client.rs Correctly wires XiaomiMimo into all three reasoning-effort branches (off/low-high/xhigh-max) with only the thinking field set and no reasoning_effort key; matching test coverage confirms behaviour
crates/config/src/lib.rs Full provider plumbing: enum variant, serde aliases, parse/as_str, defaults, env-var overrides, and config-key mappings all added consistently and mirroring existing provider patterns
crates/tui/src/config.rs TUI-side provider enum, capability metadata (1M context / 128K output, thinking on, cache off), model completion names, and env override paths added correctly
crates/cli/src/lib.rs CLI provider arg, PROVIDER_LIST size update, env-var slot, and TUI forwarding allowlist all correctly updated for XiaomiMimo
crates/secrets/src/lib.rs Env-for lookup maps all MiMo name aliases to ["XIAOMI_MIMO_API_KEY", "MIMO_API_KEY"]; test coverage confirms alias resolution
crates/agent/src/lib.rs Adds two MiMo model entries (mimo-v2.5-pro as default with mimo alias, mimo-v2.5 with xiaomi-mimo-v2.5 alias); both with tools and reasoning enabled

Flowchart

%%{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 --> D5
Loading

Fix All in Codex Fix All in Claude Code Fix All in Cursor

Reviews (1): Last reviewed commit: "feat: add Xiaomi MiMo provider" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

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.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +79 to +82
if let Some(object) = body.as_object_mut() {
object.remove("max_tokens");
}
body["max_completion_tokens"] = json!(max_tokens);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
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));
}

Comment on lines +81 to +82
domain.eq_ignore_ascii_case("xiaomimimo.com")
|| domain.to_ascii_lowercase().ends_with(".xiaomimimo.com")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
domain.eq_ignore_ascii_case("xiaomimimo.com")
|| domain.to_ascii_lowercase().ends_with(".xiaomimimo.com")
domain == "xiaomimimo.com" || domain.ends_with(".xiaomimimo.com")

Comment on lines +73 to +83
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")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Fix in Codex Fix in Claude Code Fix in Cursor

@Hmbown
Copy link
Copy Markdown
Owner

Hmbown commented May 27, 2026

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 https://token-plan-cn.xiaomimimo.com/v1 (CN-region gateway). The public/international endpoint is https://api.xiaomimimo.com/v1. Using a CN-only URL as the hardcoded default will silently break for users outside mainland China. Fix: swap the default; document the CN variant as an override.

max_completion_tokens: MiMo rejects max_tokens and expects max_completion_tokens. This PR doesn't add the field-swap call. Without it, every inference request gets a 400. The fix is a small helper that removes max_tokens and writes max_completion_tokens before the request is sent (identical to what v0.8.48 implements via apply_provider_token_limit).

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 reasoning_effort_uses_xiaomi_mimo_thinking_parameter_only covering all effort levels and the off-path. This PR has env-var guard tests but nothing that exercises the thinking-param mapping.

v0.8.48 (#2256) compatibility: Direct conflict — both add a Xiaomi MiMo variant to ApiProvider, but with different names (Xiaomi here vs XiaomiMimo there), different TOML keys (xiaomi vs xiaomi_mimo), and different default URLs. v0.8.48's version is more complete. If #2256 merges first this PR will need a full rebase and rename, or should be superseded.

Sister-PR (#2240) relationship: #2240 (HUQIANTAO) and #2246 (AdityaVG13) are functionally identical — same variant name, same base URL, same reasoning branches, no max_completion_tokens fix. Both share the same gaps. Consider consolidating into whichever is cleaner to rebase, crediting both contributors.

@Hmbown
Copy link
Copy Markdown
Owner

Hmbown commented May 27, 2026

Xiaomi MiMo cluster coordination — three implementations in flight:

This PR's reasoning_effort branches + provider_passes_model_through carve-out are the most complete on the wire-format side. The CN-vs-public base URL choice is the live question for non-CN users — your api.xiaomimimo.com/v1 is the right default for international reach if that's actually the public endpoint (worth one curl to confirm). Tests from this PR specifically (reasoning_effort_uses_xiaomi_mimo_thinking_parameter_only) are unique vs the other two implementations.

Cross-pinging @AdityaVG13 @HUQIANTAO — converging plan being figured out. Your enum-name + URL choice is the live merit question.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants