Skip to main content

rig_core/providers/
minimax.rs

1//! MiniMax API clients and Rig integrations.
2//!
3//! MiniMax exposes both OpenAI-compatible and Anthropic-compatible chat APIs,
4//! with distinct global and China entrypoints.
5//!
6//! # OpenAI-compatible example
7//! ```no_run
8//! use rig_core::client::CompletionClient;
9//! use rig_core::providers::minimax;
10//!
11//! let client = minimax::Client::new("YOUR_API_KEY").expect("Failed to build client");
12//! let model = client.completion_model(minimax::MINIMAX_M2_7);
13//! ```
14//!
15//! # Anthropic-compatible example
16//! ```no_run
17//! use rig_core::client::CompletionClient;
18//! use rig_core::providers::minimax;
19//!
20//! let client = minimax::AnthropicClient::new("YOUR_API_KEY").expect("Failed to build client");
21//! let model = client.completion_model(minimax::MINIMAX_M2);
22//! ```
23
24use crate::client;
25use crate::providers::internal::anthropic_compatible::{
26    AnthropicBaseUrl, impl_dual_dialect_provider,
27};
28
29/// Global OpenAI-compatible base URL.
30pub const GLOBAL_API_BASE_URL: &str = "https://api.minimax.io/v1";
31/// China OpenAI-compatible base URL.
32pub const CHINA_API_BASE_URL: &str = "https://api.minimaxi.com/v1";
33/// Global Anthropic-compatible base URL.
34pub const GLOBAL_ANTHROPIC_API_BASE_URL: &str = "https://api.minimax.io/anthropic";
35/// China Anthropic-compatible base URL.
36pub const CHINA_ANTHROPIC_API_BASE_URL: &str = "https://api.minimaxi.com/anthropic";
37
38/// `MiniMax-M2.7`
39pub const MINIMAX_M2_7: &str = "MiniMax-M2.7";
40/// `MiniMax-M2.7-highspeed`
41pub const MINIMAX_M2_7_HIGHSPEED: &str = "MiniMax-M2.7-highspeed";
42/// `MiniMax-M2.5`
43pub const MINIMAX_M2_5: &str = "MiniMax-M2.5";
44/// `MiniMax-M2.5-highspeed`
45pub const MINIMAX_M2_5_HIGHSPEED: &str = "MiniMax-M2.5-highspeed";
46/// `MiniMax-M2.1`
47pub const MINIMAX_M2_1: &str = "MiniMax-M2.1";
48/// `MiniMax-M2.1-highspeed`
49pub const MINIMAX_M2_1_HIGHSPEED: &str = "MiniMax-M2.1-highspeed";
50/// `MiniMax-M2`
51pub const MINIMAX_M2: &str = "MiniMax-M2";
52
53impl_dual_dialect_provider!(
54    ext = MiniMaxExt,
55    builder = MiniMaxBuilder,
56    anthropic_ext = MiniMaxAnthropicExt,
57    anthropic_builder = MiniMaxAnthropicBuilder,
58    client_input = client::BearerAuth,
59    api_key_env = "MINIMAX_API_KEY",
60    base_url = GLOBAL_API_BASE_URL,
61    base_url_env = "MINIMAX_API_BASE",
62    anthropic_provider_name = "minimax",
63    anthropic_base_url = GLOBAL_ANTHROPIC_API_BASE_URL,
64    anthropic_base_url_env = "MINIMAX_ANTHROPIC_API_BASE",
65);
66
67client::impl_capabilities!(
68    MiniMaxExt,
69    completion = super::openai::completion::GenericCompletionModel<MiniMaxExt, H>,
70    model_listing = MiniMaxModelLister<H>,
71);
72
73crate::providers::internal::model_listing::impl_model_lister!(
74    /// [`ModelLister`](crate::client::ModelLister) implementation for the
75    /// MiniMax API (`GET /models`).
76    ///
77    /// MiniMax documents the OpenAI-style `{"object":"list","data":[…]}`
78    /// envelope with `id`, `created` and `owned_by` on each entry.
79    MiniMaxModelLister,
80    Client<H>,
81    crate::providers::internal::model_listing::ListModelEntry,
82    "MiniMax",
83    "/models"
84);
85
86impl super::openai::completion::OpenAICompatibleProvider for MiniMaxExt {
87    const PROVIDER_NAME: &'static str = "minimax";
88
89    type StreamingUsage = super::openai::Usage;
90
91    type Response = super::openai::CompletionResponse;
92}
93
94const ANTHROPIC_BASE_URLS: AnthropicBaseUrl = AnthropicBaseUrl::new(
95    &[
96        (GLOBAL_API_BASE_URL, GLOBAL_ANTHROPIC_API_BASE_URL),
97        (CHINA_API_BASE_URL, CHINA_ANTHROPIC_API_BASE_URL),
98    ],
99    &["/v1", "/v1/"],
100    "/anthropic",
101);
102
103impl<H> ClientBuilder<H> {
104    pub fn global(self) -> Self {
105        self.base_url(GLOBAL_API_BASE_URL)
106    }
107
108    pub fn china(self) -> Self {
109        self.base_url(CHINA_API_BASE_URL)
110    }
111}
112
113impl<H> AnthropicClientBuilder<H> {
114    pub fn global(self) -> Self {
115        self.base_url(GLOBAL_ANTHROPIC_API_BASE_URL)
116    }
117
118    pub fn china(self) -> Self {
119        self.base_url(CHINA_ANTHROPIC_API_BASE_URL)
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::{
126        ANTHROPIC_BASE_URLS, CHINA_ANTHROPIC_API_BASE_URL, CHINA_API_BASE_URL,
127        GLOBAL_ANTHROPIC_API_BASE_URL, GLOBAL_API_BASE_URL,
128    };
129
130    #[test]
131    fn test_client_initialization() {
132        let _client = crate::providers::minimax::Client::new("dummy-key").expect("Client::new()");
133        let _client_from_builder = crate::providers::minimax::Client::builder()
134            .api_key("dummy-key")
135            .build()
136            .expect("Client::builder()");
137        let _anthropic_client = crate::providers::minimax::AnthropicClient::new("dummy-key")
138            .expect("AnthropicClient::new()");
139        let _anthropic_client_from_builder = crate::providers::minimax::AnthropicClient::builder()
140            .api_key("dummy-key")
141            .build()
142            .expect("AnthropicClient::builder()");
143    }
144
145    #[test]
146    fn normalize_openai_bases_to_anthropic_bases() {
147        assert_eq!(
148            ANTHROPIC_BASE_URLS
149                .normalize(GLOBAL_API_BASE_URL)
150                .as_deref(),
151            Some(GLOBAL_ANTHROPIC_API_BASE_URL)
152        );
153        assert_eq!(
154            ANTHROPIC_BASE_URLS.normalize(CHINA_API_BASE_URL).as_deref(),
155            Some(CHINA_ANTHROPIC_API_BASE_URL)
156        );
157        assert_eq!(
158            ANTHROPIC_BASE_URLS
159                .normalize("https://proxy.example.com/v1")
160                .as_deref(),
161            Some("https://proxy.example.com/anthropic")
162        );
163    }
164
165    #[test]
166    fn normalize_preserves_existing_anthropic_base() {
167        assert_eq!(
168            ANTHROPIC_BASE_URLS
169                .normalize(CHINA_ANTHROPIC_API_BASE_URL)
170                .as_deref(),
171            Some(CHINA_ANTHROPIC_API_BASE_URL)
172        );
173    }
174
175    #[test]
176    fn anthropic_primary_override_wins() {
177        let override_url = ANTHROPIC_BASE_URLS.resolve(
178            Some("https://primary.example.com/anthropic"),
179            Some(CHINA_API_BASE_URL),
180        );
181
182        assert_eq!(
183            override_url.as_deref(),
184            Some("https://primary.example.com/anthropic")
185        );
186    }
187}