diff --git a/src/chat_with_audio.js b/src/chat_with_audio.js
index 73735f0..bbe0d4d 100644
--- a/src/chat_with_audio.js
+++ b/src/chat_with_audio.js
@@ -2,62 +2,61 @@
import { requestLLMStream } from './llm_stream.js';
import { requestMinimaxi } from './minimaxi_stream.js';
+import { getLLMConfig, getMinimaxiConfig, getAudioConfig, validateConfig } from './config.js';
-async function chatWithAudioStream({ userInput, llmApiKey, llmModel, minimaxiApiKey, minimaxiGroupId }) {
+// 防止重复播放的标志
+let isPlaying = false;
+
+async function chatWithAudioStream(userInput) {
+ // 验证配置
+ if (!validateConfig()) {
+ throw new Error('配置不完整,请检查config.js文件中的API密钥设置');
+ }
+
console.log('用户输入:', userInput);
+ // 获取配置
+ const llmConfig = getLLMConfig();
+ const minimaxiConfig = getMinimaxiConfig();
+ const audioConfig = getAudioConfig();
+
// 1. 请求大模型回答
console.log('\n=== 请求大模型回答 ===');
const llmResponse = await requestLLMStream({
- apiKey: llmApiKey,
- model: llmModel,
+ apiKey: llmConfig.apiKey,
+ model: llmConfig.model,
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: userInput },
],
});
- // 提取大模型回答内容(假设返回的是JSON格式,包含content字段)
- let llmContent = '';
- try {
- const llmData = JSON.parse(llmResponse);
- llmContent = llmData.choices?.[0]?.message?.content || llmResponse;
- } catch (e) {
- llmContent = llmResponse;
- }
+ // 提取大模型回答内容(现在直接返回内容)
+ const llmContent = llmResponse;
console.log('\n=== 大模型回答 ===');
- console.log(llmContent);
+ console.log("llmResponse: ", llmContent);
// 2. 合成音频
console.log('\n=== 开始合成音频 ===');
const audioResult = await requestMinimaxi({
- apiKey: minimaxiApiKey,
- groupId: minimaxiGroupId,
+ apiKey: minimaxiConfig.apiKey,
+ groupId: minimaxiConfig.groupId,
body: {
- model: 'speech-02-hd',
+ model: audioConfig.model,
text: llmContent,
- stream: true,
- language_boost: 'auto',
- output_format: 'hex',
- voice_setting: {
- voice_id: 'male-qn-qingse',
- speed: 1,
- vol: 1,
- pitch: 0,
- emotion: 'happy',
- },
- audio_setting: {
- sample_rate: 32000,
- bitrate: 128000,
- format: 'mp3',
- },
+ stream: audioConfig.stream,
+ language_boost: audioConfig.language_boost,
+ output_format: audioConfig.output_format,
+ voice_setting: audioConfig.voiceSetting,
+ audio_setting: audioConfig.audioSetting,
},
stream: true,
});
// 3. 流式播放音频
console.log('\n=== 开始流式播放音频 ===');
+ // console.log('音频数据长度:', audioResult.data.audio.length);
await playAudioStream(audioResult.data.audio);
return {
@@ -69,6 +68,16 @@ async function chatWithAudioStream({ userInput, llmApiKey, llmModel, minimaxiApi
// 流式播放音频
async function playAudioStream(audioHex) {
+ if (isPlaying) {
+ console.log('音频正在播放中,跳过重复播放');
+ return;
+ }
+
+ console.log('=== 开始播放音频 ===');
+ console.log('音频数据长度:', audioHex.length);
+
+ isPlaying = true;
+
// 将hex转换为ArrayBuffer
const audioBuffer = hexToArrayBuffer(audioHex);
@@ -93,11 +102,13 @@ async function playAudioStream(audioHex) {
return new Promise((resolve) => {
source.onended = () => {
console.log('音频播放完成');
+ isPlaying = false;
resolve();
};
});
} catch (error) {
console.error('音频播放失败:', error);
+ isPlaying = false;
throw error;
}
}
@@ -113,17 +124,23 @@ function hexToArrayBuffer(hex) {
// 在Node.js环境下的音频播放(使用play-sound库)
async function playAudioStreamNode(audioHex) {
- const fs = require('fs');
- const path = require('path');
-
- // 将hex转换为buffer
- const audioBuffer = Buffer.from(audioHex, 'hex');
-
- // 保存为临时文件
- const tempFile = path.join(process.cwd(), 'temp_audio.mp3');
- fs.writeFileSync(tempFile, audioBuffer);
+ // 检查是否在Node.js环境中
+ if (typeof window !== 'undefined') {
+ console.warn('playAudioStreamNode 只能在Node.js环境中使用');
+ return;
+ }
try {
+ const fs = require('fs');
+ const path = require('path');
+
+ // 将hex转换为buffer
+ const audioBuffer = Buffer.from(audioHex, 'hex');
+
+ // 保存为临时文件
+ const tempFile = path.join(process.cwd(), 'temp_audio.mp3');
+ fs.writeFileSync(tempFile, audioBuffer);
+
// 使用系统默认播放器播放
const { exec } = require('child_process');
const platform = process.platform;
@@ -158,27 +175,4 @@ async function playAudioStreamNode(audioHex) {
}
}
-// 示例用法
-if (require.main === module) {
- const llmApiKey = process.env.ARK_API_KEY;
- const llmModel = 'bot-20250720193048-84fkp';
- const minimaxiApiKey = process.env.MINIMAXI_API_KEY;
- const minimaxiGroupId = process.env.MINIMAXI_GROUP_ID;
-
- if (!llmApiKey || !minimaxiApiKey || !minimaxiGroupId) {
- console.error('请设置环境变量: ARK_API_KEY, MINIMAXI_API_KEY, MINIMAXI_GROUP_ID');
- process.exit(1);
- }
-
- const userInput = process.argv[2] || '你好,请介绍一下人工智能的发展历程';
-
- chatWithAudioStream({
- userInput,
- llmApiKey,
- llmModel,
- minimaxiApiKey,
- minimaxiGroupId,
- }).catch(console.error);
-}
-
export { chatWithAudioStream, playAudioStream, playAudioStreamNode };
\ No newline at end of file
diff --git a/src/config.example.js b/src/config.example.js
new file mode 100644
index 0000000..dca8101
--- /dev/null
+++ b/src/config.example.js
@@ -0,0 +1,94 @@
+// 示例配置文件 - 请复制此文件为 config.js 并填入实际的API密钥
+export const config = {
+ // LLM API配置
+ llm: {
+ apiKey: 'your_ark_api_key_here', // 请替换为实际的ARK API密钥
+ model: 'bot-20250720193048-84fkp',
+ },
+
+ // Minimaxi API配置
+ minimaxi: {
+ apiKey: 'your_minimaxi_api_key_here', // 请替换为实际的Minimaxi API密钥
+ groupId: 'your_minimaxi_group_id_here', // 请替换为实际的Minimaxi Group ID
+ },
+
+ // 音频配置
+ audio: {
+ model: 'speech-02-hd',
+ voiceSetting: {
+ voice_id: 'yantu-qinggang',
+ speed: 1,
+ vol: 1,
+ pitch: 0,
+ emotion: 'happy',
+ },
+ audioSetting: {
+ sample_rate: 32000,
+ bitrate: 128000,
+ format: 'mp3',
+ },
+ },
+
+ // 系统配置
+ system: {
+ language_boost: 'auto',
+ output_format: 'hex',
+ stream: true,
+ },
+};
+
+// 验证配置是否完整
+export function validateConfig() {
+ const requiredFields = [
+ 'llm.apiKey',
+ 'llm.model',
+ 'minimaxi.apiKey',
+ 'minimaxi.groupId'
+ ];
+
+ const missingFields = [];
+
+ for (const field of requiredFields) {
+ const keys = field.split('.');
+ let value = config;
+ for (const key of keys) {
+ value = value[key];
+ if (!value) break;
+ }
+
+ if (!value || value === 'your_ark_api_key_here' || value === 'your_minimaxi_api_key_here' || value === 'your_minimaxi_group_id_here') {
+ missingFields.push(field);
+ }
+ }
+
+ if (missingFields.length > 0) {
+ console.warn('配置不完整,请检查以下字段:', missingFields);
+ return false;
+ }
+
+ return true;
+}
+
+// 获取配置的便捷方法
+export function getLLMConfig() {
+ return {
+ apiKey: config.llm.apiKey,
+ model: config.llm.model,
+ };
+}
+
+export function getMinimaxiConfig() {
+ return {
+ apiKey: config.minimaxi.apiKey,
+ groupId: config.minimaxi.groupId,
+ };
+}
+
+export function getAudioConfig() {
+ return {
+ model: config.audio.model,
+ voiceSetting: config.audio.voiceSetting,
+ audioSetting: config.audio.audioSetting,
+ ...config.system,
+ };
+}
\ No newline at end of file
diff --git a/src/config.js b/src/config.js
new file mode 100644
index 0000000..8cb236c
--- /dev/null
+++ b/src/config.js
@@ -0,0 +1,94 @@
+// 配置管理文件
+export const config = {
+ // LLM API配置
+ llm: {
+ apiKey: 'd012651b-a65b-4b13-8ff3-cc4ff3a29783', // 请替换为实际的API密钥
+ model: 'bot-20250720193048-84fkp',
+ },
+
+ // Minimaxi API配置
+ minimaxi: {
+ apiKey: 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiLkuIrmtbfpopzpgJTnp5HmioDmnInpmZDlhazlj7giLCJVc2VyTmFtZSI6IuadqOmqpSIsIkFjY291bnQiOiIiLCJTdWJqZWN0SUQiOiIxNzI4NzEyMzI0OTc5NjI2ODM5IiwiUGhvbmUiOiIxMzM4MTU1OTYxOCIsIkdyb3VwSUQiOiIxNzI4NzEyMzI0OTcxMjM4MjMxIiwiUGFnZU5hbWUiOiIiLCJNYWlsIjoiIiwiQ3JlYXRlVGltZSI6IjIwMjUtMDYtMTYgMTY6Mjk6NTkiLCJUb2tlblR5cGUiOjEsImlzcyI6Im1pbmltYXgifQ.D_JF0-nO89NdMZCYq4ocEyqxtZ9SeEdtMvbeSkZTWspt0XfX2QpPAVh-DI3MCPZTeSmjNWLf4fA_Th2zpVrj4UxWMbGKBeLZWLulNpwAHGMUTdqenuih3daCDPCzs0duhlFyQnZgGcEOGQ476HL72N2klujP8BUy_vfAh_Zv0po-aujQa5RxardDSOsbs49NTPEw0SQEXwaJ5bVmiZ5s-ysJ9pZWSEiyJ6SX9z3JeZHKj9DxHdOw5roZR8izo54e4IoqyLlzEfhOMW7P15-ffDH3M6HGiEmeBaGRYGAIciELjZS19ONNMKsTj-wXNGWtKG-sjAB1uuqkkT5Ul9Dunw', // 请替换为实际的API密钥
+ groupId: '1728712324971238231', // 请替换为实际的Group ID
+ },
+
+ // 音频配置
+ audio: {
+ model: 'speech-02-hd',
+ voiceSetting: {
+ voice_id: 'yantu-qinggang',
+ speed: 1,
+ vol: 1,
+ pitch: 0,
+ emotion: 'happy',
+ },
+ audioSetting: {
+ sample_rate: 32000,
+ bitrate: 128000,
+ format: 'mp3',
+ },
+ },
+
+ // 系统配置
+ system: {
+ language_boost: 'auto',
+ output_format: 'hex',
+ stream: true,
+ },
+};
+
+// 验证配置是否完整
+export function validateConfig() {
+ const requiredFields = [
+ 'llm.apiKey',
+ 'llm.model',
+ 'minimaxi.apiKey',
+ 'minimaxi.groupId'
+ ];
+
+ const missingFields = [];
+
+ for (const field of requiredFields) {
+ const keys = field.split('.');
+ let value = config;
+ for (const key of keys) {
+ value = value[key];
+ if (!value) break;
+ }
+
+ if (!value || value === 'your_ark_api_key_here' || value === 'your_minimaxi_api_key_here' || value === 'your_minimaxi_group_id_here') {
+ missingFields.push(field);
+ }
+ }
+
+ if (missingFields.length > 0) {
+ console.warn('配置不完整,请检查以下字段:', missingFields);
+ return false;
+ }
+
+ return true;
+}
+
+// 获取配置的便捷方法
+export function getLLMConfig() {
+ return {
+ apiKey: config.llm.apiKey,
+ model: config.llm.model,
+ };
+}
+
+export function getMinimaxiConfig() {
+ return {
+ apiKey: config.minimaxi.apiKey,
+ groupId: config.minimaxi.groupId,
+ };
+}
+
+export function getAudioConfig() {
+ return {
+ model: config.audio.model,
+ voiceSetting: config.audio.voiceSetting,
+ audioSetting: config.audio.audioSetting,
+ ...config.system,
+ };
+}
\ No newline at end of file
diff --git a/src/debug_audio.js b/src/debug_audio.js
new file mode 100644
index 0000000..7a5b669
--- /dev/null
+++ b/src/debug_audio.js
@@ -0,0 +1,26 @@
+// 调试音频数据
+function debugAudioData(audioHex) {
+ console.log('=== 音频数据调试 ===');
+ console.log('音频数据长度:', audioHex.length);
+ console.log('音频数据前100个字符:', audioHex.substring(0, 100));
+ console.log('音频数据后100个字符:', audioHex.substring(audioHex.length - 100));
+
+ // 检查是否有重复模式
+ const halfLength = Math.floor(audioHex.length / 2);
+ const firstHalf = audioHex.substring(0, halfLength);
+ const secondHalf = audioHex.substring(halfLength);
+
+ if (firstHalf === secondHalf) {
+ console.log('⚠️ 警告:音频数据可能是重复的!');
+ } else {
+ console.log('✅ 音频数据没有重复');
+ }
+}
+
+// 如果在浏览器环境中运行
+if (typeof window !== 'undefined') {
+ window.debugAudioData = debugAudioData;
+ console.log('音频调试函数已挂载到 window.debugAudioData');
+}
+
+export { debugAudioData };
\ No newline at end of file
diff --git a/src/index.html b/src/index.html
index b5f8049..c729b17 100644
--- a/src/index.html
+++ b/src/index.html
@@ -77,6 +77,6 @@
-
+