feat: 支持 MiniMax 音色克隆和 2.8 语音模型

This commit is contained in:
2026-06-17 02:13:21 +08:00
parent 02ba5d3cdd
commit c4341335d7
20 changed files with 1645 additions and 10 deletions
+82
View File
@@ -182,6 +182,88 @@ func TestMinimaxClientSpeechUsesT2AV2AndNormalizesAudio(t *testing.T) {
}
}
func TestMinimaxVoiceCloneTextValidationPayload(t *testing.T) {
var capturedClone map[string]any
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if got := r.Header.Get("Authorization"); got != "Bearer test-key" {
t.Fatalf("unexpected auth header: %q", got)
}
w.Header().Set("Content-Type", "application/json")
switch r.URL.Path {
case "/files/upload":
_ = json.NewEncoder(w).Encode(map[string]any{
"file": map[string]any{"file_id": "123456"},
"base_resp": map[string]any{"status_code": 0},
})
case "/voice_clone":
if err := json.NewDecoder(r.Body).Decode(&capturedClone); err != nil {
t.Fatalf("decode voice clone request: %v", err)
}
_ = json.NewEncoder(w).Encode(map[string]any{
"demo_audio": "",
"base_resp": map[string]any{"status_code": 0},
})
default:
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.String())
}
}))
defer server.Close()
_, err := (MinimaxClient{HTTPClient: server.Client()}).Run(context.Background(), Request{
Kind: "voice.clone",
Model: "MiniMax-Voice-Clone",
Body: map[string]any{
"voice_id": "voice_test_123",
"audio_url": "data:audio/wav;base64," + base64.StdEncoding.EncodeToString([]byte("wave")),
"text_validation": false,
"need_noise_reduction": true,
"need_volume_normalization": true,
"aigc_watermark": false,
},
Candidate: store.RuntimeModelCandidate{
Provider: "minimax",
BaseURL: server.URL,
ProviderModelName: "voice_clone",
Credentials: map[string]any{"apiKey": "test-key"},
},
})
if err != nil {
t.Fatalf("run minimax voice clone client: %v", err)
}
if _, ok := capturedClone["text_validation"]; ok {
t.Fatalf("legacy boolean text_validation should be omitted: %+v", capturedClone)
}
if capturedClone["file_id"] != float64(123456) {
t.Fatalf("file_id should be submitted as number: %+v", capturedClone)
}
capturedClone = nil
_, err = (MinimaxClient{HTTPClient: server.Client()}).Run(context.Background(), Request{
Kind: "voice.clone",
Model: "MiniMax-Voice-Clone",
Body: map[string]any{
"voice_id": "voice_test_456",
"audio_url": "data:audio/wav;base64," + base64.StdEncoding.EncodeToString([]byte("wave")),
"text_validation": " 这是一段用于校验的源音频文本 ",
},
Candidate: store.RuntimeModelCandidate{
Provider: "minimax",
BaseURL: server.URL,
ProviderModelName: "voice_clone",
Credentials: map[string]any{"apiKey": "test-key"},
},
})
if err != nil {
t.Fatalf("run minimax voice clone client with transcript: %v", err)
}
if capturedClone["text_validation"] != "这是一段用于校验的源音频文本" {
t.Fatalf("unexpected text_validation payload: %+v", capturedClone)
}
if capturedClone["file_id"] != float64(123456) {
t.Fatalf("file_id should be submitted as number with transcript: %+v", capturedClone)
}
}
func TestSimulationDurationCanBeControlledByParams(t *testing.T) {
fixedDuration := simulationDuration(Request{Body: map[string]any{"simulationDurationSeconds": 7}})
if fixedDuration != 7*time.Second {