ai_co_discuss 0.3.49 → 0.3.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai_co_discuss",
3
- "version": "0.3.49",
3
+ "version": "0.3.50",
4
4
  "bin": {
5
5
  "codiscuss": "runner/codiscuss.mjs"
6
6
  },
@@ -17,6 +17,8 @@ const VOLC_MESSAGES = {
17
17
  };
18
18
  const ALIYUN_SOCKET = 'wss://dashscope.aliyuncs.com/api-ws/v1/inference';
19
19
  const ALIYUN_MODEL = 'cosyvoice-v3-flash';
20
+ // Its default answered 160 kbps (measured 2026-11-04); 64 kbps (8 KB/s, as Volcengine) keeps a 300-character answer under the 1 MiB relay limit.
21
+ const ALIYUN_BIT_RATE = 64;
20
22
  // Aliyun and Azure answer 24 kHz mono MP3, so trailing silence is appended as silent frames of that format.
21
23
  const SAMPLE_RATE = 24000;
22
24
  // One silent MPEG-2 Layer III frame (24 kHz, 32 kbps, mono): a header and zeroed side info, 576 samples = 24 ms.
@@ -64,13 +66,13 @@ const voices = (list) => list.map(([id, label]) => ({ id, label }));
64
66
  */
65
67
  export const TTS_PROVIDERS = [
66
68
  { id: 'volcengine', label: '火山引擎', key_base_url: 'https://openspeech.bytedance.com', console_url: 'https://console.volcengine.com/speech/new',
67
- note: '豆包语音合成 2.0,约 3 元/万字(字数包更便宜);语音输入也用这个 Key。', voices: voices(VOLC_VOICES),
69
+ note: '约 3 元/万字,语音输入共用此 Key', voices: voices(VOLC_VOICES),
68
70
  default_speaker: 'zh_female_vv_uranus_bigtts', context: true, region: false, voice_input: true },
69
71
  { id: 'aliyun', label: '阿里云百炼', key_base_url: 'https://dashscope.aliyuncs.com', console_url: 'https://bailian.console.aliyun.com/?tab=model#/api-key',
70
- note: 'CosyVoice v3-flash,1 元/万字符(一个汉字计 2 字符,约 2 元/万字)。', voices: voices(ALIYUN_VOICES),
72
+ note: '约 2 元/万字(汉字计 2 字符)', voices: voices(ALIYUN_VOICES),
71
73
  default_speaker: 'longxiaochun_v3', context: false, region: false, voice_input: false },
72
74
  { id: 'azure', label: 'Azure', key_base_url: 'https://tts.speech.microsoft.com', console_url: 'https://portal.azure.com/#create/Microsoft.CognitiveServicesSpeechServices',
73
- note: '神经语音,免费档 F0 每月 50 万字符(一个汉字计 2 字符,约 25 万字);付费档约 2 元/万字;高亮按时长估算。', voices: voices(AZURE_VOICES),
75
+ note: '每月免费约 25 万字,超出约 2 元/万字', voices: voices(AZURE_VOICES),
74
76
  default_speaker: 'zh-CN-XiaoxiaoNeural', context: false, region: true, voice_input: false },
75
77
  ];
76
78
 
@@ -147,14 +149,16 @@ function aliyun_speak({ key, text, speaker, rate, silence }, open) {
147
149
  const finish = (error, value) => {
148
150
  if (settled) return;
149
151
  settled = true; clearTimeout(timer);
150
- try { socket.close(); } catch { /* Already closed. */ }
152
+ // A close without a status code is never answered by the service, which then holds the connection open.
153
+ try { socket.close(1000); } catch { /* Already closed. */ }
151
154
  if (error) reject(error); else resolve(value);
152
155
  };
153
156
  const timer = setTimeout(() => finish(new Error('语音合成请求超时,请检查网络后重试')), TTS_TIMEOUT_MS);
154
157
  const send = (action, payload) => socket.send(JSON.stringify({ header: { action, task_id, streaming: 'duplex' }, payload }));
155
158
  socket.binaryType = 'arraybuffer';
156
159
  socket.onopen = () => send('run-task', { task_group: 'audio', task: 'tts', function: 'SpeechSynthesizer', model: ALIYUN_MODEL, input: {},
157
- parameters: { text_type: 'PlainText', voice: speaker, format: 'mp3', sample_rate: SAMPLE_RATE, rate: 1 + rate / 100, word_timestamp_enabled: true } });
160
+ parameters: { text_type: 'PlainText', voice: speaker, format: 'mp3', sample_rate: SAMPLE_RATE, bit_rate: ALIYUN_BIT_RATE, rate: 1 + rate / 100,
161
+ word_timestamp_enabled: true } });
158
162
  socket.onmessage = ({ data }) => {
159
163
  if (typeof data !== 'string') { chunks.push(Buffer.from(data)); return; }
160
164
  const { header = {}, payload = {} } = JSON.parse(data);