Skip to content

feat(tts): add MiniMax as a TTS provider#44716

Closed
bingolam wants to merge 4 commits into
openclaw:mainfrom
bingolam:feat/tts-minimax-provider
Closed

feat(tts): add MiniMax as a TTS provider#44716
bingolam wants to merge 4 commits into
openclaw:mainfrom
bingolam:feat/tts-minimax-provider

Conversation

@bingolam

Copy link
Copy Markdown

Summary

Adds MiniMax T2A V2 as a fourth TTS provider alongside OpenAI, ElevenLabs, and Edge TTS.

  • New provider id: "minimax"
  • API: POST https://api.minimax.chat/v1/t2a_v2
  • Auth: MINIMAX_API_KEY env var or tts.minimax.apiKey config
  • Default model: speech-2.8-hd
  • Default voice: male-qn-qingse
  • Supported directives: [[tts:provider=minimax]], [[tts:minimax_voice=<id>]], [[tts:minimax_emotion=<value>]]
  • Supported emotions: happy/sad/angry/fearful/disgusted/surprised/calm/fluent/whisper
  • Config fields: model, voice, speed (0.5-2.0), vol (0-10], pitch (-12 to 12), emotion, outputFormat (mp3/pcm/flac/wav), sampleRate

Changes

  • src/config/types.tts.ts — extend TtsProvider union; add minimax config block
  • src/config/zod-schema.core.ts — add MiniMax Zod validation
  • src/tts/tts-core.ts — implement minimaxTTS(); add directives
  • src/tts/tts.ts — wire MiniMax into provider loop and config resolution
  • src/gateway/server-methods/tts.ts — expose hasMiniMaxKey and provider list
  • src/tts/tts.test.ts — unit tests for config defaults, env key, directives

Test plan

  • pnpm test src/tts/tts.test.ts — all tests pass
  • Set MINIMAX_API_KEY and run tts.convert via gateway

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: M labels Mar 13, 2026
@greptile-apps

greptile-apps Bot commented Mar 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds MiniMax T2A V2 as a fourth TTS provider. The implementation is largely well-structured — config types, Zod validation, directive parsing, and the core minimaxTTS function are all correct. However, there is one critical logic bug: textToSpeechTelephony has no handler for provider === "minimax" and silently falls through to the OpenAI code path, sending the MiniMax API key to the OpenAI endpoint — telephony will fail for any user whose only configured TTS provider is MiniMax.

  • Bug (critical): textToSpeechTelephony in src/tts/tts.ts falls through to the OpenAI code path when MiniMax is active, using the wrong credentials and endpoint.
  • Doc/schema mismatch (minor): The vol JSDoc says 0–10 but the Zod schema uses .positive() (excludes 0); these should be aligned.

Confidence Score: 2/5

  • Not safe to merge without fixing the telephony fallthrough bug that causes MiniMax to use the wrong API key and endpoint.
  • The non-telephony path (textToSpeech) is correctly implemented and the config/validation work is solid, but textToSpeechTelephony has a clear logic error where MiniMax falls through to the OpenAI code path. This will silently fail or corrupt requests for telephony callers using MiniMax as their primary provider, which is a regression risk.
  • src/tts/tts.ts — specifically the textToSpeechTelephony function which needs a MiniMax branch or explicit unsupported error.

Comments Outside Diff (1)

  1. src/tts/tts.ts, line 836-883 (link)

    MiniMax silently falls through to OpenAI in telephony

    textToSpeechTelephony has no provider === "minimax" branch. When MiniMax is the active provider, execution falls through to the OpenAI code path after the elevenlabs block. The MiniMax API key (resolved by resolveTtsApiKey(config, "minimax")) is then passed directly into openaiTTS with config.openai.baseUrl and config.openai.model. The OpenAI endpoint will reject the MiniMax API key, causing telephony to fail for any user whose only configured provider is MiniMax.

    A dedicated MiniMax branch (similar to the one in textToSpeech) needs to be added here, or MiniMax should be explicitly pushed aside with an "unsupported for telephony" error like Edge TTS.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/tts/tts.ts
    Line: 836-883
    
    Comment:
    **MiniMax silently falls through to OpenAI in telephony**
    
    `textToSpeechTelephony` has no `provider === "minimax"` branch. When MiniMax is the active provider, execution falls through to the OpenAI code path after the `elevenlabs` block. The MiniMax API key (resolved by `resolveTtsApiKey(config, "minimax")`) is then passed directly into `openaiTTS` with `config.openai.baseUrl` and `config.openai.model`. The OpenAI endpoint will reject the MiniMax API key, causing telephony to fail for any user whose only configured provider is MiniMax.
    
    A dedicated MiniMax branch (similar to the one in `textToSpeech`) needs to be added here, or MiniMax should be explicitly pushed aside with an `"unsupported for telephony"` error like Edge TTS.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/tts/tts.ts
Line: 836-883

Comment:
**MiniMax silently falls through to OpenAI in telephony**

`textToSpeechTelephony` has no `provider === "minimax"` branch. When MiniMax is the active provider, execution falls through to the OpenAI code path after the `elevenlabs` block. The MiniMax API key (resolved by `resolveTtsApiKey(config, "minimax")`) is then passed directly into `openaiTTS` with `config.openai.baseUrl` and `config.openai.model`. The OpenAI endpoint will reject the MiniMax API key, causing telephony to fail for any user whose only configured provider is MiniMax.

A dedicated MiniMax branch (similar to the one in `textToSpeech`) needs to be added here, or MiniMax should be explicitly pushed aside with an `"unsupported for telephony"` error like Edge TTS.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/config/types.tts.ts
Line: 81

Comment:
**`vol` doc says 0 is valid; Zod schema excludes it**

The JSDoc comment says `Volume (0–10, default 1.0)`, implying `0` is a valid lower bound. However, the Zod schema uses `z.number().positive()` which means `> 0` (strictly), so `0` would be rejected at validation time.

Either the comment should be updated to reflect that the minimum is `> 0` (e.g., `(0–10]`), or the Zod schema should use `z.number().min(0)` if `0` is intentionally supported.

```suggestion
    /** Volume (>0–10, default 1.0). Values must be strictly positive. */
    vol?: number;
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 307f981

Comment thread src/config/types.tts.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 307f981ecc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/tts/tts.ts
林冰 and others added 4 commits March 15, 2026 16:08
Adds MiniMax (https://api.minimax.chat) as a first-class TTS provider
alongside ElevenLabs, OpenAI, and Edge TTS.

- `TtsProvider` union extended with `"minimax"`
- `messages.tts.minimax` config block: apiKey, model, voice, speed, vol,
  sampleRate
- `MINIMAX_API_KEY` env var picked up automatically (same pattern as
  ELEVENLABS_API_KEY / OPENAI_API_KEY)
- Auto-detection: minimax is selected when only its API key is present
  (priority: openai > elevenlabs > minimax > edge)
- `minimaxTTS()` in tts-core.ts calls the `/v1/t2a_v2` endpoint and
  decodes the hex-encoded MP3 from the response
- Per-reply voice/model overrides via `TtsDirectiveOverrides.minimax`
- `tts.providers` gateway response lists minimax with its two models
- `tts.setProvider` accepts `"minimax"`
- `tts.status` exposes `hasMiniMaxKey`
- 3 new unit tests in tts.test.ts covering defaults, env key pickup,
  and config overrides

Defaults: model=speech-02-hd, voice=male-qn-qingse, sampleRate=32000

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…rmat

Follow-up to the initial MiniMax provider addition:

Config (`messages.tts.minimax`):
- Add `pitch` (−12–12 semitones, default 0)
- Add `emotion` (happy/sad/angry/fearful/disgusted/surprised/calm/fluent/whisper)
- Add `outputFormat` (mp3/pcm/flac/wav, default "mp3") — no longer hard-coded
- Fix `vol` Zod validation to (0, 10] per API spec (was 0.5–2.0)
- Update default model to `speech-02-hd` (alias for latest speech-2.x series)

Directive system (`[[tts:...]]`):
- `provider=minimax` now accepted when `allowProvider` is enabled
- `minimax_voice=<id>` — set voice per reply (`allowVoice` gate)
- `minimax_voice_id=<id>` — alias for minimax_voice
- `minimax_emotion=<value>` — set emotion per reply (`allowVoiceSettings` gate);
  invalid values produce a warning and are ignored

API correctness:
- `bitrate` is only sent for mp3 (not pcm/flac/wav)
- `emotion` is omitted from the request body when not set
- File extension derived from `outputFormat` (`.mp3/.pcm/.flac/.wav`)
- `voiceCompatible` always false for MiniMax (no opus support)

Gateway: `tts.providers` lists speech-2.8-hd/turbo and speech-2.6-hd/turbo

Tests: 5 new cases covering minimax provider directive, minimax_voice,
minimax_emotion (valid and invalid), totalling 47 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
speech-02-hd is an alias that may not always point to the latest model;
use the explicit speech-2.8-hd version instead.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Fix vol JSDoc to say '>0-10' (strictly positive, matching Zod .positive())
- Exclude MiniMax from textToSpeechTelephony fallback chain; MiniMax does
  not implement telephony PCM output and would otherwise fall through to the
  OpenAI branch with the wrong API key
@bingolam
bingolam force-pushed the feat/tts-minimax-provider branch from e882b82 to 7896d55 Compare March 15, 2026 08:09

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7896d550b3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/tts/tts.ts
}

export const TTS_PROVIDERS = ["openai", "elevenlabs", "edge"] as const;
export const TTS_PROVIDERS = ["openai", "elevenlabs", "minimax", "edge"] as const;

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.

P2 Badge Sync chat /tts provider command with new provider list

After adding minimax to the global provider order, the in-chat control surface still hardcodes openai|elevenlabs|edge and rejects /tts provider minimax in handleTtsCommands (src/auto-reply/reply/commands-tts.ts). That means users who manage TTS via chat commands cannot select the newly added provider even though the backend now supports it, so the feature is only partially reachable depending on which interface they use.

Useful? React with 👍 / 👎.

@steipete

steipete commented Apr 4, 2026

Copy link
Copy Markdown
Contributor

Closing as superseded by the landed MiniMax speech-provider implementation on current main.

MiniMax TTS is already in-tree through the bundled MiniMax plugin path:

  • extensions/minimax/speech-provider.ts
  • extensions/minimax/tts.ts
  • extensions/minimax/index.ts
  • docs/tools/tts.md

That landed on the extension-owned/provider-owned surface rather than the older core src/tts/* path proposed here, so this PR no longer makes sense to merge in parallel.

Thanks for putting this together.

@steipete steipete closed this Apr 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants