diff --git a/backend/app.py b/backend/app.py
index bba4d57..f20f0c1 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -4,13 +4,15 @@ AI Chat App - 后台管理服务
端口: 19020 (与前端同一端口)
"""
-from flask import Flask, jsonify, request, send_from_directory
+from flask import Flask, jsonify, request, send_from_directory, Response
from flask_cors import CORS
import os
import json
import sqlite3
from datetime import datetime
import hashlib
+import asyncio
+import edge_tts
app = Flask(__name__, static_folder='../www')
CORS(app)
@@ -244,6 +246,8 @@ def init_db():
('guest_chat_messages', '20', '游客每日对话消息限制'),
('guest_agent_messages', '20', '游客每日智能体消息限制'),
('admin_password', 'admin123', '管理员密码'),
+ ('tts_provider', 'edge', 'TTS方案'),
+ ('tts_voice', 'zh-CN-XiaoxiaoNeural', 'TTS语音'),
]
for key, value, desc in default_configs:
cursor.execute('INSERT INTO system_configs (key, value, description) VALUES (?, ?, ?)', (key, value, desc))
@@ -996,13 +1000,63 @@ def get_frontend_config():
'chatSessions': int(system.get('guest_chat_sessions', '1')),
'chatMessages': int(system.get('guest_chat_messages', '20')),
'agentMessages': int(system.get('guest_agent_messages', '20')),
- }
+ },
+ 'ttsProvider': system.get('tts_provider', 'edge'),
+ 'ttsVoice': system.get('tts_voice', 'zh-CN-XiaoxiaoNeural'),
}
}
return jsonify(config)
+# ==================== TTS 语音合成 ====================
+
+@app.route('/api/tts', methods=['POST'])
+def generate_tts():
+ """使用 Edge TTS 生成语音"""
+ data = request.json
+ text = data.get('text', '')
+ voice = data.get('voice', 'zh-CN-XiaoxiaoNeural') # 默认中文女声
+
+ if not text:
+ return jsonify({'error': '缺少文本内容'}), 400
+
+ try:
+ # 使用 asyncio 运行 edge_tts
+ async def generate_audio():
+ communicate = edge_tts.Communicate(text, voice)
+ audio_data = b''
+ for chunk in communicate.stream_sync():
+ if chunk['type'] == 'audio':
+ audio_data += chunk['data']
+ return audio_data
+
+ audio_data = asyncio.run(generate_audio())
+
+ # 返回音频数据(MP3格式)
+ return Response(audio_data, mimetype='audio/mpeg')
+
+ except Exception as e:
+ return jsonify({'error': f'TTS生成失败: {str(e)}'}), 500
+
+
+@app.route('/api/tts/voices', methods=['GET'])
+def get_tts_voices():
+ """获取可用的 TTS 语音列表"""
+ try:
+ voices = asyncio.run(edge_tts.list_voices())
+ # 过滤中文语音
+ chinese_voices = [v for v in voices if v['Locale'].startswith('zh-')]
+ voice_list = [{
+ 'name': v['ShortName'],
+ 'gender': v['Gender'],
+ 'locale': v['Locale']
+ } for v in chinese_voices]
+ return jsonify({'voices': voice_list})
+ except Exception as e:
+ return jsonify({'error': f'获取语音列表失败: {str(e)}'}), 500
+
+
# ==================== 启动 ====================
if __name__ == '__main__':
diff --git a/backend/requirements.txt b/backend/requirements.txt
index 1a72c35..d90c687 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -1,2 +1,3 @@
flask>=2.0.0
-flask-cors>=3.0.0
\ No newline at end of file
+flask-cors>=3.0.0
+edge-tts>=6.0.0
\ No newline at end of file
diff --git a/www/admin.js b/www/admin.js
index a215d65..6dba838 100644
--- a/www/admin.js
+++ b/www/admin.js
@@ -1264,6 +1264,25 @@ async function loadSystemPage(content) {
+
TTS语音配置
+
+
+
+
+
+
+
+
+
+
+
@@ -1282,7 +1301,9 @@ async function saveSystemConfig() {
guest_chat_sessions: document.getElementById('guestChatSessions').value,
guest_chat_messages: document.getElementById('guestChatMessages').value,
guest_agent_messages: document.getElementById('guestAgentMessages').value,
- admin_password: document.getElementById('adminPassword').value
+ admin_password: document.getElementById('adminPassword').value,
+ tts_provider: document.getElementById('ttsProvider').value,
+ tts_voice: document.getElementById('ttsVoice').value
};
await fetchAPI('/api/admin/system', 'POST', data);
diff --git a/www/app.js b/www/app.js
index 0ce4852..233680c 100644
--- a/www/app.js
+++ b/www/app.js
@@ -123,6 +123,11 @@ async function loadBackendConfig() {
console.log('LLM能力: 思考模式=', llmCapabilities.thinking, '视觉=', llmCapabilities.vision);
}
+ // 加载 TTS 配置
+ if (backendConfig.system) {
+ ttsVoice = backendConfig.system.ttsVoice || 'zh-CN-XiaoxiaoNeural';
+ }
+
updateAgentsDisplay();
console.log('后台配置已加载', backendConfig);
} catch (e) {
@@ -296,6 +301,13 @@ let llmCapabilities = {
vision: false // 是否支持视觉能力
};
+// TTS 语音播放状态
+let enableTTS = false; // 是否启用语音播放(新建对话默认关闭)
+let currentPlayingAudio = null; // 当前播放的音频对象
+let ttsVoice = 'zh-CN-XiaoxiaoNeural'; // TTS 语音
+let ttsQueue = []; // TTS 待播放队列
+let isTTSPlaying = false; // 是否正在播放队列
+
// DOM 元素(初始为 null,在 openConversation 时重新获取)
let appContainer = null;
let messagesContainer = null;
@@ -2915,6 +2927,9 @@ function showAgentChatPage() {
+
@@ -3033,6 +3048,21 @@ function showAgentChatPage() {
});
}
+ // 绑定 TTS 开关按钮(智能体对话)
+ const ttsBtn = document.getElementById('ttsBtn');
+ if (ttsBtn) {
+ ttsBtn.addEventListener('click', () => {
+ enableTTS = !enableTTS;
+ ttsBtn.classList.toggle('active', enableTTS);
+ showToast(enableTTS ? '语音播放已开启' : '语音播放已关闭');
+ // 如果关闭,停止当前播放
+ if (!enableTTS && currentPlayingAudio) {
+ currentPlayingAudio.pause();
+ currentPlayingAudio = null;
+ }
+ });
+ }
+
// 绑定输入事件
userInput.addEventListener('keydown', handleKeyDown);
userInput.addEventListener('input', (e) => autoResize(e.target));
@@ -3380,8 +3410,8 @@ function openConversation(id) {
🤖
${escapeHtml(currentConversation.title)}
-