Sfoglia il codice sorgente

fix: 阿里云 CosyVoice TTS 请求体格式修复

- 删除 input.format 和 input.sample_rate 字段(新 API 报 400 InvalidParameter)
- 用 tonggan 项目验证的最小请求体: model + input.text + input.voice
- endpoint 保留 /api/v1/services/audio/tts/SpeechSynthesizer (tonggan 用的)
- 阿里云默认返回 wav 格式,下载后扩展名 .mp3 由浏览器自动识别
MyFramework User 2 mesi fa
parent
commit
519e05a36c
1 ha cambiato i file con 18 aggiunte e 8 eliminazioni
  1. 18 8
      server/src/modules/tts/aliyun.provider.ts

+ 18 - 8
server/src/modules/tts/aliyun.provider.ts

@@ -76,7 +76,9 @@ export class AliyunTtsProvider implements ITtsProvider {
 
     for (let attempt = 1; attempt <= retries; attempt++) {
       try {
-        // 构建请求体
+        // 构建请求体(参考 tonggan/scripts/generate-tts.mjs 的成功调用格式)
+        // ⚠️ 重要:cosyvoice 不传 format/sample_rate,否则返回 400 InvalidParameter
+        // 阿里云百炼新版本会自动用 wav 格式(.wav 扩展名)作为默认
         const requestBody: any = {
           model: activeModel,
           input: {
@@ -86,10 +88,7 @@ export class AliyunTtsProvider implements ITtsProvider {
           },
         };
         if (isCosyVoice) {
-          requestBody.input.format = 'mp3';
-          requestBody.input.sample_rate = 24000;
-          // Instruct 情感控制:只支持固定格式 设置情感:{emotion}。
-          // CosyVoice v3-flash 系统音色不支持通过 instruction 控制语速/音调/音量
+          // CosyVoice v3-flash 系统音色支持 instruction 情感控制(固定格式 设置情感:{emotion}。)
           if ((params as any).instructText) {
             requestBody.input.instruction = (params as any).instructText;
           }
@@ -114,8 +113,12 @@ export class AliyunTtsProvider implements ITtsProvider {
         }
 
         // 千问: 同步非流式
+        // 根据模型动态选择 endpoint
+        const apiPath = activeModel.includes('cosyvoice')
+          ? 'https://dashscope.aliyuncs.com/api/v1/services/audio/tts/SpeechSynthesizer'
+          : 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation';
         const response = await withAiLog(
-          () => axios.post(this.ttsApiPath, requestBody, {
+          () => axios.post(apiPath, requestBody, {
             headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' },
             timeout: 600000,
           }),
@@ -168,8 +171,15 @@ export class AliyunTtsProvider implements ITtsProvider {
    * CosyVoice SSE 流式合成:边生成边接收,无超时限制
    */
   private async synthesizeStream(requestBody: any, outputPath: string): Promise<string> {
-    console.log(`🔊 [Aliyun SSE] 请求: ${this.ttsApiPath}, model=${this.modelId}, voice=${requestBody.input?.voice || 'default'}, textLen=${requestBody.input?.text?.length || 0}`);
-    const response = await axios.post(this.ttsApiPath, requestBody, {
+    // 根据模型动态选择 endpoint:
+    // - cosyvoice-* → /api/v1/services/audio/tts/SpeechSynthesizer
+    // - qwen3-tts-* → /api/v1/services/aigc/multimodal-generation/generation
+    const modelName = requestBody.model || this.modelId;
+    const apiPath = modelName.includes('cosyvoice')
+      ? 'https://dashscope.aliyuncs.com/api/v1/services/audio/tts/SpeechSynthesizer'
+      : 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation';
+    console.log(`🔊 [Aliyun SSE] 请求: ${apiPath}, model=${modelName}, voice=${requestBody.input?.voice || 'default'}, textLen=${requestBody.input?.text?.length || 0}`);
+    const response = await axios.post(apiPath, requestBody, {
       headers: {
         'Authorization': `Bearer ${this.apiKey}`,
         'Content-Type': 'application/json',