迁移音频生成与语音合成到 gateway 并补充 simulation 测试

This commit is contained in:
2026-06-07 10:26:57 +08:00
parent 78ab867a9f
commit dc14866210
22 changed files with 2475 additions and 55 deletions
+102
View File
@@ -2,6 +2,7 @@ package clients
import (
"context"
"encoding/base64"
"encoding/json"
"net/http"
"net/http/httptest"
@@ -65,6 +66,35 @@ func TestSimulationClientReturnsVideoDemoAssets(t *testing.T) {
}
}
func TestSimulationClientReturnsAudioDemoAssets(t *testing.T) {
response, err := (SimulationClient{}).Run(context.Background(), Request{
Kind: "speech.generations",
ModelType: "text_to_speech",
Model: "speech-2.6-turbo",
Body: map[string]any{
"text": "hello from simulation",
"voice_id": "female-shaonv",
"count": 2,
"simulationDurationMs": 5,
},
Candidate: store.RuntimeModelCandidate{Provider: "simulation"},
})
if err != nil {
t.Fatalf("run simulation audio client: %v", err)
}
data, _ := response.Result["data"].([]any)
if len(data) != 2 || response.Result["status"] != "success" {
t.Fatalf("unexpected simulated audio response: %+v", response.Result)
}
item, _ := data[0].(map[string]any)
if item["type"] != "audio" || item["url"] != "/static/simulation/audio.wav" || item["audio_url"] != "/static/simulation/audio.wav" {
t.Fatalf("unexpected simulated audio item: %+v", item)
}
if item["revised_text"] != "hello from simulation" || item["assetSource"] != "simulation" {
t.Fatalf("unexpected simulated audio metadata: %+v", item)
}
}
func TestSimulationDurationDefaultsByMediaType(t *testing.T) {
imageDuration := simulationDuration(Request{Kind: "images.generations"})
if imageDuration < 10*time.Second || imageDuration > 30*time.Second {
@@ -74,12 +104,84 @@ func TestSimulationDurationDefaultsByMediaType(t *testing.T) {
if videoDuration < 2*time.Minute || videoDuration > 3*time.Minute {
t.Fatalf("video simulation duration should default to 2-3m, got %s", videoDuration)
}
audioDuration := simulationDuration(Request{Kind: "speech.generations"})
if audioDuration < 2*time.Second || audioDuration > 6*time.Second {
t.Fatalf("audio simulation duration should default to 2-6s, got %s", audioDuration)
}
textDuration := simulationDuration(Request{Kind: "chat.completions"})
if textDuration < 800*time.Millisecond || textDuration > 2400*time.Millisecond {
t.Fatalf("text simulation duration should keep short defaults, got %s", textDuration)
}
}
func TestMinimaxClientSpeechUsesT2AV2AndNormalizesAudio(t *testing.T) {
var captured map[string]any
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost || r.URL.Path != "/t2a_v2" {
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.String())
}
if got := r.Header.Get("Authorization"); got != "Bearer test-key" {
t.Fatalf("unexpected auth header: %q", got)
}
if err := json.NewDecoder(r.Body).Decode(&captured); err != nil {
t.Fatalf("decode request: %v", err)
}
w.Header().Set("x-request-id", "req-minimax-speech")
_ = json.NewEncoder(w).Encode(map[string]any{
"data": map[string]any{"audio": "68656c6c6f"},
"base_resp": map[string]any{"status_code": 0},
})
}))
defer server.Close()
response, err := (MinimaxClient{HTTPClient: server.Client()}).Run(context.Background(), Request{
Kind: "speech.generations",
Model: "MiniMax Speech 2.6 Turbo",
Body: map[string]any{
"text": "hello",
"voice_id": "female-shaonv",
"speed": 1.2,
"vol": 0.8,
"pitch": -1,
"emotion": "happy",
},
Candidate: store.RuntimeModelCandidate{
Provider: "minimax",
BaseURL: server.URL,
ProviderModelName: "speech-2.6-turbo",
Credentials: map[string]any{"apiKey": "test-key"},
},
})
if err != nil {
t.Fatalf("run minimax speech client: %v", err)
}
if captured["model"] != "speech-2.6-turbo" || captured["text"] != "hello" {
t.Fatalf("unexpected minimax speech payload: %+v", captured)
}
if _, ok := captured["voice_id"]; ok {
t.Fatalf("voice_id should be moved into voice_setting: %+v", captured)
}
voiceSetting, ok := captured["voice_setting"].(map[string]any)
if !ok {
t.Fatalf("missing voice_setting: %+v", captured)
}
if voiceSetting["voice_id"] != "female-shaonv" || voiceSetting["speed"] != 1.2 || voiceSetting["vol"] != 0.8 || voiceSetting["pitch"] != float64(-1) || voiceSetting["emotion"] != "happy" {
t.Fatalf("unexpected voice_setting: %+v", voiceSetting)
}
data, _ := response.Result["data"].([]any)
if len(data) != 1 {
t.Fatalf("unexpected minimax speech response: %+v", response.Result)
}
item, _ := data[0].(map[string]any)
expectedContent := "data:audio/mpeg;base64," + base64.StdEncoding.EncodeToString([]byte("hello"))
if item["type"] != "audio" || item["content"] != expectedContent || item["mime_type"] != "audio/mpeg" {
t.Fatalf("unexpected normalized audio item: %+v", item)
}
if response.RequestID != "req-minimax-speech" {
t.Fatalf("unexpected request id: %q", response.RequestID)
}
}
func TestSimulationDurationCanBeControlledByParams(t *testing.T) {
fixedDuration := simulationDuration(Request{Body: map[string]any{"simulationDurationSeconds": 7}})
if fixedDuration != 7*time.Second {