feat: 添加realtime_dialog和realtime_dialog_external_rag_test项目,更新test2项目

This commit is contained in:
User
2026-03-13 13:06:46 +08:00
parent 9dab61345c
commit 5521b673f5
215 changed files with 7626 additions and 1876 deletions

View File

@@ -1,4 +1,5 @@
require('dotenv').config();
const http = require('http');
const express = require('express');
const cors = require('cors');
const path = require('path');
@@ -6,14 +7,11 @@ const db = require('./db');
const voiceRoutes = require('./routes/voice');
const chatRoutes = require('./routes/chat');
const sessionRoutes = require('./routes/session');
const { setupNativeVoiceGateway } = require('./services/nativeVoiceGateway');
// ========== 环境变量校验 ==========
function validateEnv() {
const required = [
{ key: 'VOLC_RTC_APP_ID', desc: 'RTC 应用 ID' },
{ key: 'VOLC_RTC_APP_KEY', desc: 'RTC 应用密钥' },
{ key: 'VOLC_ACCESS_KEY_ID', desc: '火山引擎 AccessKey ID' },
{ key: 'VOLC_SECRET_ACCESS_KEY', desc: '火山引擎 Secret Access Key' },
{ key: 'VOLC_S2S_APP_ID', desc: 'S2S 端到端语音 AppID' },
{ key: 'VOLC_S2S_TOKEN', desc: 'S2S 端到端语音 Token' },
{ key: 'VOLC_ARK_ENDPOINT_ID', desc: '方舟 LLM 推理接入点 ID' },
@@ -68,42 +66,11 @@ function validateEnv() {
// ========== Express 应用 ==========
const app = express();
const server = http.createServer(app);
const PORT = process.env.PORT || 3001;
app.use(cors());
// RTC Function Calling 回调不带 Content-Type必须在标准 body parser 之前手动读取
// 全局序列号:在 body 读取前同步分配,确保反映真实请求到达顺序
let fcCallbackSeq = 0;
app.post('/api/voice/fc_callback', (req, res, next) => {
const seq = ++fcCallbackSeq; // 同步分配,在任何异步操作之前
if (!req.headers['content-type']) {
const chunks = [];
req.on('data', (chunk) => chunks.push(chunk));
req.on('end', () => {
const rawBuf = Buffer.concat(chunks);
const raw = rawBuf.toString('utf-8');
console.log(`[RawBody] seq=${seq} Read ${rawBuf.length} bytes`);
// 将所有回调原始内容追加到日志文件
try {
const fs = require('fs');
fs.appendFileSync('fc_all_callbacks.log', `\n=== SEQ=${seq} TIME=${new Date().toISOString()} BYTES=${rawBuf.length} ===\n${raw}\n`);
} catch(e) {}
try { req.body = JSON.parse(raw); } catch (e) { console.error('[RawBody] JSON parse failed:', e.message); req.body = { _raw: raw }; }
req.body._seq = seq;
next();
});
req.on('error', (err) => {
console.error('[RawBody] Error:', err.message);
next();
});
} else {
req.body = req.body || {};
req.body._seq = seq;
next();
}
});
app.use(express.json({ limit: '1mb' }));
app.use(express.urlencoded({ extended: true, limit: '1mb' }));
@@ -123,16 +90,8 @@ app.use('/api/voice', voiceRoutes);
app.use('/api/chat', chatRoutes);
app.use('/api/session', sessionRoutes);
// 静态文件服务
app.use(express.static('../client/dist'));
// 处理单页应用路由
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../client/dist/index.html'));
});
app.get('/api/health', (req, res) => {
const envReady = !process.env.VOLC_RTC_APP_ID?.startsWith('your_');
const envReady = !process.env.VOLC_S2S_APP_ID?.startsWith('your_');
res.json({
status: 'ok',
mode: 's2s-hybrid',
@@ -149,6 +108,14 @@ app.get('/api/health', (req, res) => {
});
});
// 静态文件服务
app.use(express.static('../client/dist'));
// 处理单页应用路由
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../client/dist/index.html'));
});
// 统一错误处理中间件
app.use((err, req, res, _next) => {
console.error(`[Error] ${req.method} ${req.path}:`, err.message);
@@ -174,7 +141,14 @@ async function start() {
console.warn('[DB] Continuing without database — context switching will use in-memory fallback');
}
app.listen(PORT, () => {
if (process.env.ENABLE_NATIVE_VOICE_GATEWAY !== 'false') {
setupNativeVoiceGateway(server);
console.log('[NativeVoice] Gateway enabled at /ws/realtime-dialog');
} else {
console.log('[NativeVoice] Gateway disabled (ENABLE_NATIVE_VOICE_GATEWAY=false)');
}
server.listen(PORT, () => {
console.log('\n========================================');
console.log(` 🚀 Voice Chat Backend`);
console.log(` 📡 http://localhost:${PORT}`);