Skip to content

feat: add Xiaomi MiMo provider support#2240

Open
HUQIANTAO wants to merge 11 commits into
Hmbown:mainfrom
HUQIANTAO:feat/xiaomi-mimo-support
Open

feat: add Xiaomi MiMo provider support#2240
HUQIANTAO wants to merge 11 commits into
Hmbown:mainfrom
HUQIANTAO:feat/xiaomi-mimo-support

Conversation

@HUQIANTAO
Copy link
Copy Markdown

@HUQIANTAO HUQIANTAO commented May 27, 2026

Summary

  • Add first-class Xiaomi MiMo provider support with dedicated API integration via token-plan endpoint (https://token-plan-cn.xiaomimimo.com/v1)
  • Support two MiMo models: mimo-v2.5-pro (reasoning flagship) and mimo-v2.5 (omni)
  • Implement MiMo-specific thinking toggle (thinking: { type: "enabled"/"disabled" }) instead of DeepSeek-style reasoning_effort
  • Enable /models command for Xiaomi provider to fetch available models from the API
  • Add provider-specific model picker UI with MiMo models and simplified thinking effort (off/high)

Changes

Provider registration:

  • crates/config/src/lib.rsProviderKind::Xiaomi variant, default model/base_url constants, parse/as_str
  • crates/tui/src/config.rsApiProvider::Xiaomi variant, provider_capability (thinking enabled), all match arms
  • crates/cli/src/lib.rsProviderArg::Xiaomi, provider list, env var mapping

Model picker & validation:

  • crates/tui/src/tui/model_picker.rs — Provider-specific model lists (PICKER_MODELS_XIAOMI) and thinking effort lists (PICKER_EFFORTS_MIMO with only off/high)
  • crates/tui/src/config.rsnormalize_model_name_for_provider accepts MiMo model names for Xiaomi provider

API integration:

  • crates/tui/src/client.rsapply_reasoning_effort sends thinking: { type: "disabled" } for off, thinking: { type: "enabled" } for high/max
  • crates/tui/src/client/chat.rsrequires_reasoning_content() includes mimo prefix for reasoning token replay
  • crates/tui/src/tui/ui.rs — Allow /models command for Xiaomi provider

Model registry:

  • crates/agent/src/lib.rsModelInfo entries for mimo-v2.5-pro and mimo-v2.5 with aliases

Config & docs:

  • config.example.toml[providers.xiaomi] section with token-plan URL and model examples
  • .env.exampleMIMO_API_KEY, MIMO_BASE_URL, MIMO_MODEL documentation

Test plan

  • cargo fmt --all -- --check passes
  • cargo clippy --workspace --all-targets --all-features --locked -- -D warnings passes
  • cargo test --workspace --all-features --locked passes (4 pre-existing failures unrelated to this change)
  • Manual TUI test: /provider → Xiaomi MiMo, /model → mimo-v2.5-pro, send a message
  • Manual TUI test: /model → switch thinking effort off/high, verify API accepts the request
  • Manual TUI test: /models → verify model list is fetched from Xiaomi API
  • Manual TUI test: restart app → verify Xiaomi provider and MiMo model are persisted

Greptile Summary

This PR adds first-class Xiaomi MiMo provider support, wiring ProviderKind::Xiaomi / ApiProvider::Xiaomi through the full stack: config/secrets/CLI registration, TUI provider and model pickers, apply_reasoning_effort (MiMo-specific thinking: {type: "enabled"/"disabled"} toggle), reasoning-content replay for multi-turn tool calls, and all match exhaustiveness sites.

  • Adds mimo-v2.5-pro and mimo-v2.5 to 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).
  • Introduces MIMO_API_KEY, MIMO_BASE_URL, MIMO_MODEL env vars, [providers.xiaomi] TOML section, codewhale auth set --provider xiaomi, and /models command support for the Xiaomi endpoint.
  • docs/PROVIDERS.md documents that the MiMo API uses max_completion_tokens instead of max_tokens, but both the non-streaming (create_message_chat) and streaming (handle_chat_completion_stream) request-body constructors in chat.rs always 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.md entry added in this same PR explicitly states that the MiMo API uses max_completion_tokens instead of max_tokens. Both the non-streaming and streaming request paths in chat.rs always send "max_tokens" with no Xiaomi-specific branch to rename the field. If the MiMo API does not honor max_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_tokens instead of max_tokens.

Important Files Changed

Filename Overview
crates/tui/src/client/chat.rs Adds MiMo reasoning-content replay and thinking-effort wiring; both non-streaming and streaming paths send max_tokens even though PROVIDERS.md documents MiMo uses max_completion_tokens.
crates/tui/src/client.rs Adds Xiaomi-specific apply_reasoning_effort arms for off/high/max, sending thinking: { type: "disabled"/"enabled" } without reasoning_effort. Unit tests cover all four effort cases.
crates/tui/src/config.rs Adds ApiProvider::Xiaomi throughout; model completion returns [mimo-v2.5-pro, mimo-v2.5]; provider_capability sets 1M context/128K output; provider_passes_model_through includes Xiaomi.
crates/tui/src/tui/model_picker.rs Refactors picker to provider-specific model and effort lists; PICKER_MODELS_XIAOMI has auto/mimo-v2.5-pro/mimo-v2.5; PICKER_EFFORTS_MIMO has Off/High; pass-through providers retain the 'auto' row.
crates/config/src/lib.rs Adds ProviderKind::Xiaomi with aliases, default constants, ProvidersToml field, and env-var handling (MIMO_API_KEY, MIMO_BASE_URL).
crates/agent/src/lib.rs Registers mimo-v2.5-pro and mimo-v2.5 ModelInfo entries with aliases; both have supports_reasoning=true and supports_tools=true.
crates/tui/src/tui/ui.rs Allows /models for Xiaomi by excluding it from the pass-through provider guard; adds Xiaomi to provider-picker api_key and auth_mode update paths.
docs/PROVIDERS.md Adds xiaomi row documenting max_completion_tokens usage — this documented behavior is not implemented in chat.rs where max_tokens is always sent.

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 response
Loading

Comments Outside Diff (1)

  1. crates/tui/src/client/chat.rs, line 83 (link)

    P1 max_tokens field not renamed for Xiaomi MiMo

    The PR's own docs/PROVIDERS.md entry for xiaomi explicitly documents that the MiMo API uses max_completion_tokens instead of max_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 in handle_chat_completion_stream.

    Fix in Codex Fix in Claude Code Fix in Cursor

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

Reviews (5): Last reviewed commit: "fix(mimo): address review feedback on Xi..." | Re-trigger Greptile

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

Comment on lines +92 to +96
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,
};
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.

high

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.

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

Comment on lines +121 to +124
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
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

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);

Comment thread crates/tui/src/config.rs Outdated
Comment on lines +451 to +459
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;
}
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

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;
    }

Comment thread crates/tui/src/tui/model_picker.rs
Comment thread crates/tui/src/tui/model_picker.rs
Comment thread crates/tui/src/config.rs Outdated
Hmbown added a commit that referenced this pull request May 27, 2026
- 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>
Hu Qiantao and others added 8 commits May 27, 2026 10:56
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>
@HUQIANTAO HUQIANTAO force-pushed the feat/xiaomi-mimo-support branch from 580e90d to af98fc1 Compare May 27, 2026 02:57
Hu Qiantao and others added 2 commits May 27, 2026 14:14
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>
@Hmbown Hmbown mentioned this pull request May 27, 2026
12 tasks
@Hmbown
Copy link
Copy Markdown
Owner

Hmbown commented May 27, 2026

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 (PICKER_EFFORTS_MIMO with only off/high) and the thinking: { type } translation across all three effort tiers (off/high/max) are correct per the documented MiMo contract. README trilingual updates are present.

Concerns:

  • Zero new tests. Sister PR feat: add Xiaomi MiMo provider #2246 ships a reasoning_effort_uses_xiaomi_mimo_thinking_parameter_only unit test covering all effort tiers + a registry resolution test. Given the three-branch divergence in apply_reasoning_effort, this is the weakest link.
  • Base URL differs from sister: this PR uses token-plan-cn.xiaomimimo.com/v1 (token-plan endpoint); feat: add Xiaomi MiMo provider #2246 uses api.xiaomimimo.com/v1. One is wrong or region-specific — needs a citation in the PR body.
  • Greptile's && !is_xiaomi carve-out comment is valid; either add the inline rationale or drop Xiaomi from provider_passes_model_through.
  • Misleading "same thinking format as DeepSeek" comment at client.rs:925 — DeepSeek also sends reasoning_effort, Xiaomi does not.

v0.8.48 (#2256) compatibility: No conflict — #2256 is unrelated (verification-gate work), does not add Xiaomi. Earlier intel was incorrect.
Sister PR #2246 relationship: Hard conflict — different enum name (Xiaomi vs XiaomiMimo), different base URL, different env var slugs, same files. Pick one; merging both is impossible. #2246 has tests + richer serde aliases; #2240 has the simpler picker UX and trilingual docs. Recommend cherry-picking tests + the XiaomiMimo naming from #2246 into this PR, or vice versa.

@Hmbown
Copy link
Copy Markdown
Owner

Hmbown commented May 27, 2026

Correction to my earlier comment: I miscalled v0.8.48's Xiaomi state. Verified just now via git show origin/feature/v0.8.48-crate-consolidation:crates/config/src/lib.rs | grep -i xiaomi:

const DEFAULT_XIAOMI_MODEL: &str = "mimo-v2.5-pro";
const DEFAULT_XIAOMI_BASE_URL: &str = "https://token-plan-cn.xiaomimimo.com/v1";
ProviderKind::Xiaomi  (with serde alias "mimo")

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 ProviderKind::Xiaomi. Same enum name, same base URL — should merge as a duplicate-add conflict that auto-resolves to identity, but the test/picker/auth wiring will need a manual 3-way merge.

@Hmbown
Copy link
Copy Markdown
Owner

Hmbown commented May 27, 2026

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 reasoning_effort_uses_xiaomi_mimo_thinking_parameter_only covering all effort tiers; this PR has zero new tests.

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