feat: add MiniMax provider support (Chat + TTS)#82
Conversation
- Add MiniMaxBackend LLM adapter using OpenAI-compatible API (models: MiniMax-M2.7, MiniMax-M2.7-highspeed) - Add MiniMaxTTSBackend for text-to-speech via MiniMax T2A API (models: speech-2.8-hd, speech-2.8-turbo) - Add MINIMAX_API_KEY environment variable support (shared by both) - Add unit tests for registration, availability, and config
📝 WalkthroughWalkthroughAdds MiniMax provider integration for LLM chat completions and TTS audio synthesis. The LLM backend uses OpenAI-compatible chat API with forced temperature=1.0. The TTS backend posts JSON to MiniMax API, decodes hex-encoded MP3 audio, and resamples to 32 kHz mono via torchaudio. Both backends register in module-level maps and include comprehensive test coverage. ChangesMiniMax Backend Support
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/services/tts_backend.py`:
- Around line 1167-1193: The code builds the TTS endpoint by unconditionally
appending "/v1/t2a_v2" to the MINIMAX_BASE_URL (variable base_url) which causes
double "/v1/v1" if MINIMAX_BASE_URL already contains a path; fix by normalizing
base_url before composing the Request: strip trailing slashes and also remove a
trailing "/v1" segment if present (or use urllib.parse.urljoin to safely join
base_url and "/v1/t2a_v2"), then use the normalized base_url in the Request call
(refer to base_url and the Request f"{base_url}/v1/t2a_v2" in tts_backend.py).
In `@tests/test_engines.py`:
- Around line 162-165: The test test_llm_minimax_default_model relies on ambient
MINIMAX_MODEL env state; before creating MiniMaxBackend() ensure the env var is
isolated by removing or overriding it (e.g., call
os.environ.pop("MINIMAX_MODEL", None) or use pytest's
monkeypatch.delenv("MINIMAX_MODEL", raising=False)) so
MiniMaxBackend().model_name equals the hardcoded default "MiniMax-M2.7"; update
the test to remove/delenv the variable just prior to instantiating
MiniMaxBackend to guarantee deterministic behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: eedf3a6b-c1e9-4dd4-940b-36a45f131a87
📒 Files selected for processing (3)
backend/services/llm_backend.pybackend/services/tts_backend.pytests/test_engines.py
| base_url = os.environ.get("MINIMAX_BASE_URL", "https://api.minimax.io") | ||
| base_url = base_url.rstrip("/") | ||
| model = os.environ.get("MINIMAX_TTS_MODEL", "speech-2.8-hd") | ||
| voice = kw.get("voice", self.VOICES[0]) | ||
| speed = float(kw.get("speed", 1.0)) | ||
|
|
||
| payload = json.dumps({ | ||
| "model": model, | ||
| "text": text, | ||
| "stream": False, | ||
| "voice_setting": { | ||
| "voice_id": voice, | ||
| "speed": speed, | ||
| "vol": 1, | ||
| "pitch": 0, | ||
| }, | ||
| "audio_setting": { | ||
| "sample_rate": self.sample_rate, | ||
| "bitrate": 128000, | ||
| "format": "mp3", | ||
| "channel": 1, | ||
| }, | ||
| }).encode() | ||
|
|
||
| req = urllib.request.Request( | ||
| f"{base_url}/v1/t2a_v2", | ||
| data=payload, |
There was a problem hiding this comment.
Normalize MINIMAX_BASE_URL before composing the TTS endpoint.
This currently appends /v1/t2a_v2 unconditionally. If MINIMAX_BASE_URL is set to https://api.minimax.io/v1 (the same shape used by the MiniMax LLM backend), requests go to /v1/v1/t2a_v2 and fail.
Suggested fix
- base_url = os.environ.get("MINIMAX_BASE_URL", "https://api.minimax.io")
- base_url = base_url.rstrip("/")
+ base_url = os.environ.get("MINIMAX_BASE_URL", "https://api.minimax.io/v1").rstrip("/")
+ endpoint = (
+ f"{base_url}/t2a_v2"
+ if base_url.endswith("/v1")
+ else f"{base_url}/v1/t2a_v2"
+ )
@@
- f"{base_url}/v1/t2a_v2",
+ endpoint,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| base_url = os.environ.get("MINIMAX_BASE_URL", "https://api.minimax.io") | |
| base_url = base_url.rstrip("/") | |
| model = os.environ.get("MINIMAX_TTS_MODEL", "speech-2.8-hd") | |
| voice = kw.get("voice", self.VOICES[0]) | |
| speed = float(kw.get("speed", 1.0)) | |
| payload = json.dumps({ | |
| "model": model, | |
| "text": text, | |
| "stream": False, | |
| "voice_setting": { | |
| "voice_id": voice, | |
| "speed": speed, | |
| "vol": 1, | |
| "pitch": 0, | |
| }, | |
| "audio_setting": { | |
| "sample_rate": self.sample_rate, | |
| "bitrate": 128000, | |
| "format": "mp3", | |
| "channel": 1, | |
| }, | |
| }).encode() | |
| req = urllib.request.Request( | |
| f"{base_url}/v1/t2a_v2", | |
| data=payload, | |
| base_url = os.environ.get("MINIMAX_BASE_URL", "https://api.minimax.io/v1").rstrip("/") | |
| endpoint = ( | |
| f"{base_url}/t2a_v2" | |
| if base_url.endswith("/v1") | |
| else f"{base_url}/v1/t2a_v2" | |
| ) | |
| model = os.environ.get("MINIMAX_TTS_MODEL", "speech-2.8-hd") | |
| voice = kw.get("voice", self.VOICES[0]) | |
| speed = float(kw.get("speed", 1.0)) | |
| payload = json.dumps({ | |
| "model": model, | |
| "text": text, | |
| "stream": False, | |
| "voice_setting": { | |
| "voice_id": voice, | |
| "speed": speed, | |
| "vol": 1, | |
| "pitch": 0, | |
| }, | |
| "audio_setting": { | |
| "sample_rate": self.sample_rate, | |
| "bitrate": 128000, | |
| "format": "mp3", | |
| "channel": 1, | |
| }, | |
| }).encode() | |
| req = urllib.request.Request( | |
| endpoint, | |
| data=payload, |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/tts_backend.py` around lines 1167 - 1193, The code builds
the TTS endpoint by unconditionally appending "/v1/t2a_v2" to the
MINIMAX_BASE_URL (variable base_url) which causes double "/v1/v1" if
MINIMAX_BASE_URL already contains a path; fix by normalizing base_url before
composing the Request: strip trailing slashes and also remove a trailing "/v1"
segment if present (or use urllib.parse.urljoin to safely join base_url and
"/v1/t2a_v2"), then use the normalized base_url in the Request call (refer to
base_url and the Request f"{base_url}/v1/t2a_v2" in tts_backend.py).
| def test_llm_minimax_default_model(): | ||
| be = llm_backend.MiniMaxBackend() | ||
| assert be.model_name == "MiniMax-M2.7" | ||
|
|
There was a problem hiding this comment.
Isolate MINIMAX_MODEL in the default-model test.
This test depends on ambient environment state and can fail when MINIMAX_MODEL is already set in CI/dev shells.
Suggested fix
-def test_llm_minimax_default_model():
+def test_llm_minimax_default_model(monkeypatch):
+ monkeypatch.delenv("MINIMAX_MODEL", raising=False)
be = llm_backend.MiniMaxBackend()
assert be.model_name == "MiniMax-M2.7"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def test_llm_minimax_default_model(): | |
| be = llm_backend.MiniMaxBackend() | |
| assert be.model_name == "MiniMax-M2.7" | |
| def test_llm_minimax_default_model(monkeypatch): | |
| monkeypatch.delenv("MINIMAX_MODEL", raising=False) | |
| be = llm_backend.MiniMaxBackend() | |
| assert be.model_name == "MiniMax-M2.7" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/test_engines.py` around lines 162 - 165, The test
test_llm_minimax_default_model relies on ambient MINIMAX_MODEL env state; before
creating MiniMaxBackend() ensure the env var is isolated by removing or
overriding it (e.g., call os.environ.pop("MINIMAX_MODEL", None) or use pytest's
monkeypatch.delenv("MINIMAX_MODEL", raising=False)) so
MiniMaxBackend().model_name equals the hardcoded default "MiniMax-M2.7"; update
the test to remove/delenv the variable just prior to instantiating
MiniMaxBackend to guarantee deterministic behavior.
|
Thanks @octo-patch — clean structure (env-gated registration, tests included, follows the existing Project-identity decision (made today after council review of the local-first question your PR raised):
Reasoning: Concretely for this PR:
Path forward: If you can split this PR into LLM-only (drop the TTS backend + its tests), I'll squash-merge the LLM side this week. The MiniMax TTS conversation isn't dead — it's a separate discussion about whether the project ever opens to cloud TTS, which deserves community input via a discussion thread rather than a code review. Happy to help open that thread if you want to start it. Thanks for your patience while we worked through the policy question — your PR forced a useful conversation. |
Summary
Add MiniMax as a new provider for both Chat (LLM) and TTS capabilities:
MiniMaxBackend— OpenAI-compatible adapter using modelsMiniMax-M2.7andMiniMax-M2.7-highspeed. Follows the same registry pattern as the existingOpenAICompatBackend.MiniMaxTTSBackend— cloud-based text-to-speech via MiniMax's T2A API (speech-2.8-hd,speech-2.8-turbo). Supports 6 English voices, hex-encoded audio decoding, and auto-appears in Settings → Engines.MINIMAX_API_KEY(get one at platform.minimax.io)Environment Variables
MINIMAX_API_KEYMINIMAX_BASE_URLMINIMAX_MODELMiniMax-M2.7)MINIMAX_TTS_MODELspeech-2.8-hd)API Documentation
Test Plan
uv run pytest tests/test_engines.py— 26 passed, 2 skipped)MiniMax-M2.7Summary by CodeRabbit
MINIMAX_API_KEYenvironment variable.MINIMAX_API_KEYand optionalMINIMAX_BASE_URLandMINIMAX_MODELvariables.