Go: implement TTS for fishaudio, openrouter and asr for fishaudio (#14926)

### What problem does this PR solve?

This PR implement TTS for FishAudio and MiniMax provider and ASR for
FishAudio

**The following functionalities are now supported:**

**FishAudio:**
- [x] Text To Speech
- [x] Stream Text To Speech
- [x] Audio To Text

**OpenRouter:**

- [x] Text To Speech

**Verified examples from the CLI:**
```plaintext

**FishAudio**

RAGFlow(user)> tts with 's1@test@fishaudio' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"reference_id": "90e65eaaf50e4470b8e6d43ee6afd7d5", "temperature": 0.7, "top_p": 0.7, "prosody": {"speed": 1, "volume": 0, "normalize_loudness": true}, "chunk_length": 300, "normalize": true, "sample_rate": 44100, "mp3_bitrate": 128, "latency": "normal", "max_new_tokens": 1024, "repetition_penalty": 1.2, "min_chunk_length": 50, "condition_on_previous_chunks": true, "early_stop_threshold": 1}'
Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/s1_output.wav
SUCCESS

RAGFlow(user)> stream tts with 's1@test@fishaudio' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"reference_id": "90e65eaaf50e4470b8e6d43ee6afd7d5", "temperature": 0.7, "top_p": 0.7, "prosody": {"speed": 1, "volume": 0, "normalize_loudness": true}, "chunk_length": 300, "normalize": true, "sample_rate": 44100, "mp3_bitrate": 128, "latency": "normal", "max_new_tokens": 1024, "repetition_penalty": 1.2, "min_chunk_length": 50, "condition_on_previous_chunks": true, "early_stop_threshold": 1}'
Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/s1_output.wav
SUCCESS

RAGFlow(user)> asr with 'transcribe-1@test@fishaudio' audio './internal/test.wav' param '{"language": "en", "ignore_timestamps": true}'
+----------------------------------------------------------------------------------------------------------------------+
| text                                                                                                                 |
+----------------------------------------------------------------------------------------------------------------------+
| The examination and testimony of the experts enabled the commission to conclude that five shots may have been fired. |
+----------------------------------------------------------------------------------------------------------------------+

```

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
- [x] New Feature (non-breaking change which adds functionality)
- [x] Refactoring
This commit is contained in:
Haruko386
2026-05-14 18:58:00 +08:00
committed by GitHub
parent a98994ff91
commit 106f4b777e
9 changed files with 436 additions and 33 deletions

View File

@@ -2013,7 +2013,7 @@ func (c *RAGFlowClient) TTSUserCommand(cmd *Command) (ResponseIf, error) {
if explicitFormat != "" {
ttsConfigPayload["format"] = explicitFormat
} else {
explicitFormat = "mp3"
ttsConfigPayload["format"] = "mp3"
}
if len(ttsConfigPayload) > 0 {
@@ -2056,7 +2056,6 @@ func (c *RAGFlowClient) TTSUserCommand(cmd *Command) (ResponseIf, error) {
shouldSave, _ := cmd.Params["save"].(bool)
saveDir, _ := cmd.Params["save_path"].(string)
fileName := fmt.Sprintf("%s_output.%s", modelName, explicitFormat)
cwd, err := os.Getwd()
@@ -2149,14 +2148,27 @@ func (c *RAGFlowClient) ASRUserCommand(cmd *Command) (ResponseIf, error) {
audioFile, ok := cmd.Params["audio_file"].(string)
if !ok {
return nil, fmt.Errorf("text not provided")
return nil, fmt.Errorf("audio file not provided")
}
payload := map[string]interface{}{
"provider_name": providerName,
"instance_name": instanceName,
"model_name": modelName,
"audio_file": audioFile,
"file": audioFile,
}
asrConfigPayload := make(map[string]interface{})
if paramStr, ok := cmd.Params["param_str"].(string); ok && paramStr != "" {
var dynamicParams map[string]interface{}
if err := json.Unmarshal([]byte(paramStr), &dynamicParams); err != nil {
return nil, fmt.Errorf("param string must be valid JSON. Error: %w", err)
}
asrConfigPayload["params"] = dynamicParams
}
if len(asrConfigPayload) > 0 {
payload["asr_config"] = asrConfigPayload
}
url := "/audio/transcriptions"
@@ -2168,13 +2180,23 @@ func (c *RAGFlowClient) ASRUserCommand(cmd *Command) (ResponseIf, error) {
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to ASR document: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result CommonResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
var rawResult struct {
Code int `json:"code"`
Message string `json:"message"`
Data map[string]interface{} `json:"data"`
}
if err = json.Unmarshal(resp.Body, &rawResult); err != nil {
return nil, fmt.Errorf("ASR document failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
if rawResult.Code != 0 {
return nil, fmt.Errorf("%s", rawResult.Message)
}
var result CommonResponse
result.Code = rawResult.Code
result.Message = rawResult.Data["text"].(string) // TODO
result.Duration = resp.Duration
return &result, nil