Skip to content

增加MiniMax语音合成TTS支持 - #2081

Merged
creamlike1024 merged 4 commits into
QuantumNous:mainfrom
feitianbubu:pr/add-miniMax-tts
Oct 20, 2025
Merged

增加MiniMax语音合成TTS支持#2081
creamlike1024 merged 4 commits into
QuantumNous:mainfrom
feitianbubu:pr/add-miniMax-tts

Conversation

@feitianbubu

@feitianbubu feitianbubu commented Oct 20, 2025

Copy link
Copy Markdown
Member

文档: https://platform.minimaxi.com/document/t2a_http?key=68ad78146fe587e3fbfe8e03
支持模型: speech-2.5-hd-preview 、 speech-2.5-turbo-preview 、 speech-02-hd 、 speech-02-turbo 、 speech-01-hd 、 speech-01-turbo
请求格式:

{
  "model": "speech-2.5-hd-preview",
  "input": "你好",
  "voice": "male-qn-qingse",
  "metadata": {
    "stream": false,
    "voice_setting": {
      "voice_id": "male-qn-qingse",
      "speed": 1,
      "vol": 1,
      "pitch": 0,
      "emotion": "happy"
    },
    "pronunciation_dict": {
      "tone": [
        "处理/(chu3)(li3)",
        "危险/dangerous"
      ]
    },
    "audio_setting": {
      "sample_rate": 32000,
      "bitrate": 128000,
      "format": "mp3",
      "channel": 1
    },
    "output_format":"hex",
    "subtitle_enable": false
  }
}

返回示例:
image

Summary by CodeRabbit

  • New Features
    • Added MiniMax provider with full text‑to‑speech support and six new speech models (multiple HD/turbo variants).
    • Audio responses now stream or redirect when appropriate and support embedded (hex) and URL-based audio delivery.
    • Usage and audio format are tracked and returned with responses.

@coderabbitai

coderabbitai Bot commented Oct 20, 2025

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

Walkthrough

Adds MiniMax channel support: new APIType constant and mapping, a minimax adaptor with request/response conversions (including TTS), new TTS types/handlers, updated model list and URL routing, and wiring the adaptor into the relay adaptor dispatcher.

Changes

Cohort / File(s) Summary
API Type & Mapping
constant/api_type.go, common/api_type.go
Adds APITypeMiniMax constant and a ChannelTypeMiniMaxAPITypeMiniMax case in the ChannelType2APIType mapping.
MiniMax Adaptor
relay/channel/minimax/adaptor.go
New exported Adaptor type with methods for converting Gemini/Claude/OpenAI/audio/image/embedding/rerank requests, request URL/header setup, DoRequest/DoResponse orchestration, and model/channel accessors.
TTS Handler & Types
relay/channel/minimax/tts.go
New MiniMax TTS request/response structs, content-type resolution, handlers for TTS and chat completion responses, audio decoding/redirect handling, and usage extraction.
Models & Routing
relay/channel/minimax/constants.go, relay/channel/minimax/relay-minimax.go
Expanded ModelList with six speech model IDs; GetRequestURL now derives baseUrl (from info or channel constants) and switches on RelayMode to return chat or TTS endpoints.
Integration Changes
relay/relay_adaptor.go, relay/channel/openai/adaptor.go
Registers minimax.Adaptor in relay adaptor switch for APITypeMiniMax; MiniMax handling commented out/disabled in OpenAI adaptor.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Relay
    participant Adaptor as MiniMax Adaptor
    participant TTS as TTS Handler
    participant MiniMax as MiniMax API

    Client->>Relay: Audio request
    Relay->>Adaptor: Route by APITypeMiniMax
    Adaptor->>Adaptor: ConvertAudioRequest (merge metadata, build TTS payload)
    Adaptor->>Adaptor: GetRequestURL (RelayModeAudioSpeech -> /v1/t2a_v2)
    Adaptor->>MiniMax: DoRequest (POST payload)
    MiniMax-->>Adaptor: HTTP response (redirect / hex / base64)
    Adaptor->>TTS: DoResponse -> handleTTSResponse
    TTS-->>Client: Stream audio (with proper Content-Type) and usage
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • seefs001

Poem

🐰 I hopped through code to add a voice,
New models hum and endpoints rejoice,
Adaptor stitches payloads neat,
TTS returns an audio treat,
Little rabbit cheers: "Now route with poise!" 🎶

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "支持MiniMax语音合成TTS" (Support MiniMax TTS) is directly and clearly related to the main objective of the changeset, which is to add support for MiniMax text-to-speech synthesis. The changes comprehensively implement this feature across multiple files, including adding MiniMax as a channel type (constant/api_type.go, common/api_type.go), creating TTS-specific handlers and request/response processing (relay/channel/minimax/tts.go, adaptor.go), adding speech model identifiers (relay/channel/minimax/constants.go), and integrating the MiniMax adaptor into the relay system (relay/relay_adaptor.go). The title is concise, specific, and clearly communicates the primary feature addition without vague terminology or irrelevant details.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7ab0f4 and e20a287.

📒 Files selected for processing (1)
  • relay/channel/minimax/adaptor.go (1 hunks)

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@feitianbubu feitianbubu changed the title 支持MiniMax语音合成TTS 增加MiniMax语音合成TTS支持 Oct 20, 2025

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (4)
relay/channel/minimax/relay-minimax.go (2)

12-15: Normalize trailing slash to avoid “//” in URLs.

Minor: trim any trailing “/” on baseUrl (both custom and default) before fmt.Sprintf.

Apply:

 import (
 	"fmt"
+	"strings"

 	channelconstant "github.com/QuantumNous/new-api/constant"
 	relaycommon "github.com/QuantumNous/new-api/relay/common"
 	"github.com/QuantumNous/new-api/relay/constant"
 )

 func GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
-	baseUrl := info.ChannelBaseUrl
+	baseUrl := strings.TrimRight(info.ChannelBaseUrl, "/")
 	if baseUrl == "" {
-		baseUrl = channelconstant.ChannelBaseURLs[channelconstant.ChannelTypeMiniMax]
+		baseUrl = strings.TrimRight(channelconstant.ChannelBaseURLs[channelconstant.ChannelTypeMiniMax], "/")
 	}

17-24: Endpoints LGTM; consider WS path for streaming TTS.

If info.IsStream, MiniMax also exposes a WS endpoint wss://…/ws/v1/t2a_v2. Consider routing to WS when streaming to reduce latency.

Apply:

 	switch info.RelayMode {
 	case constant.RelayModeChatCompletions:
 		return fmt.Sprintf("%s/v1/text/chatcompletion_v2", baseUrl), nil
 	case constant.RelayModeAudioSpeech:
-		return fmt.Sprintf("%s/v1/t2a_v2", baseUrl), nil
+		if info.IsStream {
+			wsBase := strings.Replace(strings.Replace(baseUrl, "https://", "wss://", 1), "http://", "ws://", 1)
+			return fmt.Sprintf("%s/ws/v1/t2a_v2", wsBase), nil
+		}
+		return fmt.Sprintf("%s/v1/t2a_v2", baseUrl), nil
 	default:
 		return "", fmt.Errorf("unsupported relay mode: %d", info.RelayMode)
 	}

Reference for WS path. (github.com)

relay/channel/openai/adaptor.go (1)

21-21: MiniMax decoupling verified; remove commented code blocks for cleaner state.

The routing is correctly configured:

  • relay/relay_adaptor.go:112-113 routes APITypeMiniMax to the dedicated minimax.Adaptor
  • MiniMax requests will not reach the OpenAI adaptor
  • The decoupling is complete and safe

Remove the commented import and case statements at relay/channel/openai/adaptor.go lines 21, 164-165, 602-603, and 619-620 to reduce code drift and maintainability overhead.

relay/channel/minimax/adaptor.go (1)

65-69: Add clarifying comment for outputFormat handling.

The logic modifying outputFormat after marshaling the request can be confusing. This variable is used to track the expected response format (stored in context), not the request format.

Apply this diff to clarify:

+	// Track expected response format in context (MiniMax returns "url" for most formats, "hex" for hex encoding)
 	if outputFormat != "hex" {
 		outputFormat = "url"
 	}
 
 	c.Set("response_format", outputFormat)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7156bf2 and c7ab0f4.

📒 Files selected for processing (8)
  • common/api_type.go (1 hunks)
  • constant/api_type.go (1 hunks)
  • relay/channel/minimax/adaptor.go (1 hunks)
  • relay/channel/minimax/constants.go (1 hunks)
  • relay/channel/minimax/relay-minimax.go (1 hunks)
  • relay/channel/minimax/tts.go (1 hunks)
  • relay/channel/openai/adaptor.go (4 hunks)
  • relay/relay_adaptor.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
common/api_type.go (2)
constant/channel.go (1)
  • ChannelTypeMiniMax (35-35)
constant/api_type.go (1)
  • APITypeMiniMax (36-36)
relay/relay_adaptor.go (3)
constant/api_type.go (1)
  • APITypeMiniMax (36-36)
relay/channel/minimax/adaptor.go (1)
  • Adaptor (21-22)
relay/channel/adapter.go (1)
  • Adaptor (15-32)
relay/channel/minimax/relay-minimax.go (3)
relay/common/relay_info.go (1)
  • RelayInfo (75-122)
constant/channel.go (2)
  • ChannelBaseURLs (60-117)
  • ChannelTypeMiniMax (35-35)
relay/constant/relay_mode.go (2)
  • RelayModeChatCompletions (10-10)
  • RelayModeAudioSpeech (35-35)
relay/channel/minimax/tts.go (3)
relay/common/relay_info.go (1)
  • RelayInfo (75-122)
types/error.go (5)
  • NewAPIError (87-95)
  • NewErrorWithStatusCode (259-275)
  • ErrorCodeReadResponseBodyFailed (69-69)
  • ErrorCodeBadResponseBody (72-72)
  • ErrorCodeBadResponse (71-71)
dto/openai_response.go (1)
  • Usage (222-235)
relay/channel/minimax/adaptor.go (8)
relay/channel/openai/adaptor.go (1)
  • Adaptor (33-36)
relay/common/relay_info.go (1)
  • RelayInfo (75-122)
dto/audio.go (1)
  • AudioRequest (11-20)
relay/channel/minimax/tts.go (3)
  • MiniMaxTTSRequest (18-32)
  • VoiceSetting (38-46)
  • AudioSetting (52-58)
relay/channel/minimax/relay-minimax.go (1)
  • GetRequestURL (11-25)
relay/channel/api_request.go (3)
  • SetupApiRequestHeader (27-39)
  • DoRequest (252-254)
  • DoApiRequest (61-90)
types/error.go (1)
  • NewAPIError (87-95)
relay/channel/minimax/constants.go (2)
  • ModelList (5-17)
  • ChannelName (19-19)
🔇 Additional comments (13)
relay/relay_adaptor.go (2)

21-21: Import addition is correct.

Cleanly enables adaptor wiring; no side effects.


112-113: Adaptor selection for MiniMax wired correctly.

Returns minimax.Adaptor for APITypeMiniMax; consistent with new mapping.

common/api_type.go (1)

72-73: Mapping LGTM.

ChannelTypeMiniMax correctly maps to APITypeMiniMax.

constant/api_type.go (1)

36-36: Enum insertion is safe; Dummy confirmed as sentinel-only.

Verification confirms APITypeDummy is used exclusively as a count/sentinel value in the for loop (controller/model.go:31) and is not persisted. The explicit comment in the enum definition confirms this intent. The insertion of APITypeMiniMax before APITypeDummy is correct.

relay/channel/minimax/constants.go (1)

11-16: LGTM!

The six new speech model identifiers are correctly added and match the models documented in the PR description.

relay/channel/minimax/tts.go (3)

18-91: LGTM!

The TTS request and response type definitions are well-structured with appropriate JSON tags and field types.


107-172: LGTM with one note.

The TTS response handling logic correctly:

  • Validates the response structure
  • Checks for errors via BaseResp.StatusCode
  • Handles both redirect and hex-encoded audio cases
  • Computes usage metrics

The hardcoded content type issue at line 160 is already flagged in a separate comment.


174-194: LGTM!

This handler correctly passes through the chat completion response by copying headers and returning the raw body.

relay/channel/minimax/adaptor.go (5)

21-30: LGTM!

The Adaptor struct and stub implementations for unsupported request types are appropriate.


94-111: LGTM!

The conversion methods appropriately pass through supported request types and stub out unsupported ones.


113-124: LGTM!

The request/response routing correctly delegates to the TTS handler for audio requests and falls back to the OpenAI adaptor for other relay modes.


126-132: LGTM!

The accessor methods correctly return the channel constants.


88-92: Authorization header format is correct—no changes needed.

The code sets the Authorization header as "Bearer "+info.ApiKey, which matches MiniMax API documentation that requires Authorization: Bearer <YOUR_API_KEY>. The implementation is accurate and requires no modification.

Comment thread relay/channel/minimax/adaptor.go Outdated
Comment on lines +93 to +105
func getContentTypeByFormat(format string) string {
contentTypeMap := map[string]string{
"mp3": "audio/mpeg",
"wav": "audio/wav",
"flac": "audio/flac",
"aac": "audio/aac",
"pcm": "audio/pcm",
}
if ct, ok := contentTypeMap[format]; ok {
return ct
}
return "audio/mpeg" // default to mp3
}

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.

⚠️ Potential issue | 🟠 Major

This helper function is never used.

The getContentTypeByFormat function is defined but never called. Line 160 in handleTTSResponse hardcodes contentType = "audio/mpeg" instead of using this function to determine the correct content type based on the audio format.

Apply this diff to use the helper function properly:

 	} else {
 		// Handle hex-encoded audio data
 		audioData, decodeErr := hex.DecodeString(minimaxResp.Data.Audio)
 		if decodeErr != nil {
 			return nil, types.NewErrorWithStatusCode(
 				fmt.Errorf("failed to decode hex audio data: %w", decodeErr),
 				types.ErrorCodeBadResponse,
 				http.StatusInternalServerError,
 			)
 		}
 
-		// Determine content type - default to mp3
-		contentType := "audio/mpeg"
+		// Determine content type from response format
+		format := "mp3" // default
+		if val, exists := c.Get("audio_format"); exists {
+			if f, ok := val.(string); ok {
+				format = f
+			}
+		}
+		contentType := getContentTypeByFormat(format)
 
 		c.Data(http.StatusOK, contentType, audioData)
 	}

Note: You'll also need to store the audio format in the Gin context (similar to how response_format is stored at adaptor.go line 69) so it's available when serving the response.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In relay/channel/minimax/tts.go around lines 93-105, the getContentTypeByFormat
helper is defined but unused; update handleTTSResponse (around line 160) to read
the audio format from the Gin context and call getContentTypeByFormat(format)
instead of hardcoding "audio/mpeg", and ensure the audio format is saved into
the Gin context when the response is created (follow the pattern used for
"response_format" at adaptor.go line 69) so the handler can retrieve it; this
makes content type selection dynamic and defaults as implemented in the helper.

Comment out the debug log for MiniMax TTS Request.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants