Skip to main content

codex_convert_proxy/providers/
minimax.rs

1//! MiniMax provider implementation.
2
3use crate::providers::trait_::Provider;
4use crate::types::chat_api::{ChatRequest, ChatResponse, ChatStreamChunk, Content, MessageRole};
5
6/// MiniMax provider.
7///
8/// MiniMax specific handling:
9/// - Content must be string, not array
10/// - Does not support 'developer' role, convert to 'user'
11pub struct MiniMaxProvider;
12
13impl Default for MiniMaxProvider {
14    fn default() -> Self {
15        Self
16    }
17}
18
19impl MiniMaxProvider {
20    pub fn new() -> Self {
21        Self
22    }
23}
24
25impl Provider for MiniMaxProvider {
26    fn name(&self) -> &'static str {
27        "minimax"
28    }
29
30    fn chat_completions_path(&self) -> String {
31        // MiniMax URL already includes /v1, so just /chat/completions
32        "/chat/completions".to_string()
33    }
34
35    fn transform_request(&self, request: &mut ChatRequest) {
36        // MiniMax requires content to be a string, not an array
37        for message in &mut request.messages {
38            if matches!(message.content, Content::Array(_)) {
39                let text = message.content.as_text();
40                message.content = Content::String(text);
41            }
42            // MiniMax doesn't support developer role - convert to user
43            if message.role == MessageRole::Developer {
44                message.role = MessageRole::User;
45            }
46        }
47    }
48
49    fn transform_response(&self, response: &mut ChatResponse) {
50        // Ensure content is string
51        for choice in &mut response.choices {
52            if matches!(choice.message.content, Content::Array(_)) {
53                let text = choice.message.content.as_text();
54                choice.message.content = Content::String(text);
55            }
56        }
57    }
58
59    fn transform_stream_chunk(&self, chunk: &mut ChatStreamChunk) {
60        // Ensure delta content is string
61        for choice in &mut chunk.choices {
62            if let Some(delta) = &mut choice.delta
63                && matches!(delta.content, Some(Content::Array(_)))
64                    && let Some(content) = delta.content.take() {
65                        let text = content.as_text();
66                        if !text.is_empty() {
67                            delta.content = Some(Content::String(text));
68                        }
69                    }
70        }
71    }
72
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use crate::types::chat_api::{ChatMessage, Content, MessageRole};
79
80    #[test]
81    fn test_minimax_flattens_content() {
82        let mut request = ChatRequest {
83            model: "ab-01".to_string(),
84            messages: vec![ChatMessage {
85                role: MessageRole::User,
86                content: Content::Array(vec![crate::types::chat_api::ContentBlock {
87                    block_type: "text".to_string(),
88                    text: Some("Hello".to_string()),
89                    image_url: None,
90                }]),
91                name: None,
92                annotations: None,
93                tool_calls: None,
94                tool_call_id: None,
95                function_call: None,
96                refusal: None,
97            }],
98            tools: None,
99            tool_choice: None,
100            stream: Some(false),
101            temperature: None,
102            max_tokens: None,
103            top_p: None,
104            user: None,
105            stream_options: None,
106            frequency_penalty: None,
107            presence_penalty: None,
108            logit_bias: None,
109            logprobs: None,
110            top_logprobs: None,
111            n: None,
112            stop: None,
113            response_format: None,
114            reasoning_effort: None,
115            parallel_tool_calls: None,
116            seed: None,
117            service_tier: None,
118        };
119
120        let provider = MiniMaxProvider;
121        provider.transform_request(&mut request);
122
123        let msg = request.messages.first().unwrap();
124        assert!(matches!(msg.content, Content::String(_)));
125        assert_eq!(msg.content.as_text(), "Hello");
126    }
127}