Skip to main content

rig_core/providers/
xiaomimimo.rs

1//! Xiaomi MiMo API clients and Rig integrations.
2//!
3//! Xiaomi exposes both OpenAI-compatible and Anthropic-compatible chat APIs
4//! under a single global host.
5//!
6//! # OpenAI-compatible example
7//! ```no_run
8//! use rig_core::client::CompletionClient;
9//! use rig_core::providers::xiaomimimo;
10//!
11//! let client = xiaomimimo::Client::new("YOUR_API_KEY").expect("Failed to build client");
12//! let model = client.completion_model(xiaomimimo::MIMO_V2_5_PRO);
13//! ```
14//!
15//! # Anthropic-compatible example
16//! ```no_run
17//! use rig_core::client::CompletionClient;
18//! use rig_core::providers::xiaomimimo;
19//!
20//! let client = xiaomimimo::AnthropicClient::new("YOUR_API_KEY").expect("Failed to build client");
21//! let model = client.completion_model(xiaomimimo::MIMO_V2_5_PRO);
22//! ```
23
24use crate::client;
25use crate::providers::internal::anthropic_compatible::{
26    AnthropicBaseUrl, impl_dual_dialect_provider,
27};
28use crate::providers::internal::model_listing::{ListModelEntry, impl_model_lister};
29
30/// OpenAI-compatible base URL.
31pub const API_BASE_URL: &str = "https://api.xiaomimimo.com/v1";
32/// Anthropic-compatible base URL.
33pub const ANTHROPIC_API_BASE_URL: &str = "https://api.xiaomimimo.com/anthropic/v1";
34
35/// `mimo-v2-flash`
36pub const MIMO_V2_FLASH: &str = "mimo-v2-flash";
37/// `mimo-v2-omni`
38pub const MIMO_V2_OMNI: &str = "mimo-v2-omni";
39/// `mimo-v2-pro`
40pub const MIMO_V2_PRO: &str = "mimo-v2-pro";
41/// `mimo-v2.5`
42pub const MIMO_V2_5: &str = "mimo-v2.5";
43/// `mimo-v2.5-pro`
44pub const MIMO_V2_5_PRO: &str = "mimo-v2.5-pro";
45
46impl_dual_dialect_provider!(
47    ext = XiaomiMimoExt,
48    builder = XiaomiMimoBuilder,
49    anthropic_ext = XiaomiMimoAnthropicExt,
50    anthropic_builder = XiaomiMimoAnthropicBuilder,
51    client_input = client::BearerAuth,
52    api_key_env = "XIAOMI_MIMO_API_KEY",
53    base_url = API_BASE_URL,
54    base_url_env = "XIAOMI_MIMO_API_BASE",
55    anthropic_provider_name = "xiaomimimo",
56    anthropic_base_url = ANTHROPIC_API_BASE_URL,
57    anthropic_base_url_env = "XIAOMI_MIMO_ANTHROPIC_API_BASE",
58);
59
60client::impl_capabilities!(
61    XiaomiMimoExt,
62    completion = super::openai::completion::GenericCompletionModel<XiaomiMimoExt, H>,
63    model_listing = XiaomiMimoModelLister<H>,
64);
65
66impl super::openai::completion::OpenAICompatibleProvider for XiaomiMimoExt {
67    const PROVIDER_NAME: &'static str = "xiaomimimo";
68
69    type StreamingUsage = super::openai::Usage;
70
71    type Response = super::openai::CompletionResponse;
72}
73
74const ANTHROPIC_BASE_URLS: AnthropicBaseUrl = AnthropicBaseUrl::new(
75    &[(API_BASE_URL, ANTHROPIC_API_BASE_URL)],
76    &["/v1", "/v1/"],
77    "/anthropic/v1",
78);
79
80impl_model_lister!(
81    /// [`ModelLister`](crate::client::ModelLister) implementation for the
82    /// Xiaomi MiMo API (`GET /models`).
83    XiaomiMimoModelLister,
84    Client<H>,
85    ListModelEntry,
86    "Xiaomi MiMo",
87    "/models"
88);
89
90#[cfg(test)]
91mod tests {
92    use super::{ANTHROPIC_API_BASE_URL, ANTHROPIC_BASE_URLS, API_BASE_URL};
93
94    #[test]
95    fn test_client_initialization() {
96        let _client =
97            crate::providers::xiaomimimo::Client::new("dummy-key").expect("Client::new()");
98        let _client_from_builder = crate::providers::xiaomimimo::Client::builder()
99            .api_key("dummy-key")
100            .build()
101            .expect("Client::builder()");
102        let _anthropic_client = crate::providers::xiaomimimo::AnthropicClient::new("dummy-key")
103            .expect("AnthropicClient::new()");
104        let _anthropic_client_from_builder =
105            crate::providers::xiaomimimo::AnthropicClient::builder()
106                .api_key("dummy-key")
107                .build()
108                .expect("AnthropicClient::builder()");
109    }
110
111    #[test]
112    fn normalize_openai_bases_to_anthropic_bases() {
113        assert_eq!(
114            ANTHROPIC_BASE_URLS.normalize(API_BASE_URL).as_deref(),
115            Some(ANTHROPIC_API_BASE_URL)
116        );
117        assert_eq!(
118            ANTHROPIC_BASE_URLS
119                .normalize("https://proxy.example.com/v1")
120                .as_deref(),
121            Some("https://proxy.example.com/anthropic/v1")
122        );
123    }
124
125    #[test]
126    fn normalize_preserves_existing_anthropic_base() {
127        assert_eq!(
128            ANTHROPIC_BASE_URLS
129                .normalize(ANTHROPIC_API_BASE_URL)
130                .as_deref(),
131            Some(ANTHROPIC_API_BASE_URL)
132        );
133    }
134
135    #[test]
136    fn anthropic_primary_override_wins() {
137        let override_url = ANTHROPIC_BASE_URLS.resolve(
138            Some("https://primary.example.com/anthropic/v1"),
139            Some(API_BASE_URL),
140        );
141
142        assert_eq!(
143            override_url.as_deref(),
144            Some("https://primary.example.com/anthropic/v1")
145        );
146    }
147}