Files
bigwo/test2/server/routes/voice.js

185 lines
7.1 KiB
JavaScript
Raw Normal View History

2026-03-12 12:47:56 +08:00
const express = require('express');
const router = express.Router();
const { v4: uuidv4 } = require('uuid');
const ToolExecutor = require('../services/toolExecutor');
const contextKeywordTracker = require('../services/contextKeywordTracker');
const { getRuleBasedDirectRouteDecision, shouldForceKnowledgeRoute } = require('../services/realtimeDialogRouting');
2026-03-12 12:47:56 +08:00
const DEFAULT_TOOLS = require('../config/tools');
const db = require('../db');
const directSessions = new Map();
2026-03-12 12:47:56 +08:00
router.get('/config', (req, res) => {
res.json({
success: true,
data: {
models: [
{ value: '1.2.1.0', label: 'O2.0(推荐,精品音质)' },
{ value: 'O', label: 'O基础版' },
{ value: '2.2.0.0', label: 'SC2.0(推荐,声音复刻)' },
{ value: 'SC', label: 'SC基础版' },
],
speakers: [
{ value: 'zh_female_vv_jupiter_bigtts', label: 'VV活泼女声', series: 'O' },
{ value: 'zh_female_xiaohe_jupiter_bigtts', label: '小禾(甜美女声·台湾口音)', series: 'O' },
{ value: 'zh_male_yunzhou_jupiter_bigtts', label: '云舟(沉稳男声)', series: 'O' },
{ value: 'zh_male_xiaotian_jupiter_bigtts', label: '小天(磁性男声)', series: 'O' },
{ value: 'saturn_common_female_1', label: 'Saturn 女声1', series: 'SC2.0' },
{ value: 'saturn_common_male_1', label: 'Saturn 男声1', series: 'SC2.0' },
{ value: 'ICL_common_female_1', label: 'ICL 女声1', series: 'SC' },
{ value: 'ICL_common_male_1', label: 'ICL 男声1', series: 'SC' },
],
tools: DEFAULT_TOOLS.map((t) => ({
name: t.function.name,
description: t.function.description,
})),
},
});
});
router.post('/direct/session', async (req, res) => {
2026-03-12 12:47:56 +08:00
try {
const { userId, sessionId } = req.body || {};
const sid = sessionId || uuidv4();
const directSession = {
sessionId: sid,
userId: userId || null,
2026-03-12 12:47:56 +08:00
startTime: Date.now(),
direct: true,
};
directSessions.set(sid, directSession);
await db.createSession(sid, userId || null, 'voice');
2026-03-12 12:47:56 +08:00
res.json({
success: true,
data: {
sessionId: sid,
userId: userId || null,
2026-03-12 12:47:56 +08:00
},
});
} catch (error) {
console.error('[DirectVoice] Create session failed:', error.message);
2026-03-12 12:47:56 +08:00
res.status(500).json({ success: false, error: error.message });
}
});
router.post('/direct/message', async (req, res) => {
2026-03-12 12:47:56 +08:00
try {
const { sessionId, role, text, source, toolName } = req.body || {};
if (!sessionId || !text || !source) {
return res.status(400).json({ success: false, error: 'sessionId, text and source are required' });
2026-03-12 12:47:56 +08:00
}
if (role === 'user') {
contextKeywordTracker.updateSession(sessionId, text);
}
await db.addMessage(sessionId, role === 'user' ? 'user' : 'assistant', text, source, toolName || null);
res.json({ success: true });
2026-03-12 12:47:56 +08:00
} catch (error) {
console.error('[DirectVoice] Add message failed:', error.message);
2026-03-12 12:47:56 +08:00
res.status(500).json({ success: false, error: error.message });
}
});
router.post('/diag', (req, res) => {
2026-03-12 12:47:56 +08:00
try {
const { sessionId, roomId, type, payload } = req.body || {};
console.log(`[Diag] type=${type || 'unknown'} session=${sessionId || '-'} room=${roomId || '-'} payload=${JSON.stringify(payload || {})}`);
2026-03-12 12:47:56 +08:00
res.json({ success: true });
} catch (error) {
console.error('[Diag] Error:', error.message);
2026-03-12 12:47:56 +08:00
res.status(500).json({ success: false, error: error.message });
}
});
router.post('/direct/query', async (req, res) => {
2026-03-12 12:47:56 +08:00
try {
const { sessionId, query, appendUserMessage } = req.body || {};
if (!sessionId) {
return res.status(400).json({ success: false, error: 'sessionId is required' });
2026-03-12 12:47:56 +08:00
}
const context = await db.getHistoryForLLM(sessionId, 20).catch(() => []);
const cleanQuery = (query || '').trim();
if (appendUserMessage && cleanQuery) {
contextKeywordTracker.updateSession(sessionId, cleanQuery);
await db.addMessage(sessionId, 'user', cleanQuery, 'voice_asr').catch(() => null);
}
if (!appendUserMessage && cleanQuery) {
contextKeywordTracker.updateSession(sessionId, cleanQuery);
}
const routeDecision = getRuleBasedDirectRouteDecision(cleanQuery);
const forceKb = shouldForceKnowledgeRoute(cleanQuery, context);
const shouldSearchKb = routeDecision.route === 'search_knowledge' || forceKb;
const directSession = directSessions.get(sessionId);
const result = shouldSearchKb
? await ToolExecutor.execute('search_knowledge', { query: cleanQuery, session_id: sessionId, _session: { userId: directSession?.userId || null } }, context)
: { hit: false, reason: 'route_skip', source: 'route_skip', error: '该问题不在知识库范围内,请咨询其他问题。' };
let contentText = JSON.stringify(result);
if (result && result.results && Array.isArray(result.results)) {
contentText = result.results.map((item) => item.content || JSON.stringify(item)).join('\n');
} else if (result && result.error) {
contentText = result.error;
} else if (typeof result === 'string') {
contentText = result;
}
const ragItems = result && result.results && Array.isArray(result.results) && result.results.length > 0
? result.results.map((item) => ({
title: item.title || '知识库结果',
content: item.content || JSON.stringify(item),
}))
: [{
title: '知识库结果',
content: contentText,
}];
await db.addMessage(sessionId, 'assistant', contentText, 'voice_tool', 'search_knowledge', {
route: 'search_knowledge',
original_text: cleanQuery,
tool_name: 'search_knowledge',
tool_args: { query: cleanQuery },
source: result?.source || null,
original_query: result?.original_query || cleanQuery,
rewritten_query: result?.rewritten_query || null,
selected_dataset_ids: result?.selected_dataset_ids || null,
selected_kb_routes: result?.selected_kb_routes || null,
hit: typeof result?.hit === 'boolean' ? result.hit : null,
reason: result?.reason || null,
error_type: result?.errorType || null,
latency_ms: result?.latency_ms || null,
}).catch(() => null);
res.json({
success: true,
data: {
sessionId,
query: cleanQuery,
contentText,
ragItems,
ragJson: JSON.stringify(ragItems),
},
});
2026-03-12 12:47:56 +08:00
} catch (error) {
console.error('[DirectVoice] Query failed:', error.message);
2026-03-12 12:47:56 +08:00
res.status(500).json({ success: false, error: error.message });
}
});
router.post('/direct/stop', async (req, res) => {
2026-03-12 12:47:56 +08:00
try {
const { sessionId } = req.body || {};
if (!sessionId) {
return res.status(400).json({ success: false, error: 'sessionId is required' });
2026-03-12 12:47:56 +08:00
}
directSessions.delete(sessionId);
const messages = await db.getMessages(sessionId).catch(() => []);
res.json({
success: true,
data: {
sessionId,
messageCount: messages.length,
},
2026-03-12 12:47:56 +08:00
});
} catch (error) {
console.error('[DirectVoice] Stop session failed:', error.message);
res.status(500).json({ success: false, error: error.message });
2026-03-12 12:47:56 +08:00
}
});
module.exports = router;