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 DEFAULT_TOOLS = require('../config/tools');
|
|
|
|
|
|
const db = require('../db');
|
|
|
|
|
|
|
2026-03-13 13:06:46 +08:00
|
|
|
|
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,
|
|
|
|
|
|
})),
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-13 13:06:46 +08:00
|
|
|
|
router.post('/direct/session', async (req, res) => {
|
2026-03-12 12:47:56 +08:00
|
|
|
|
try {
|
2026-03-13 13:06:46 +08:00
|
|
|
|
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(),
|
2026-03-13 13:06:46 +08:00
|
|
|
|
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: {
|
2026-03-13 13:06:46 +08:00
|
|
|
|
sessionId: sid,
|
|
|
|
|
|
userId: userId || null,
|
2026-03-12 12:47:56 +08:00
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
2026-03-13 13:06:46 +08:00
|
|
|
|
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 });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-13 13:06:46 +08:00
|
|
|
|
router.post('/direct/message', async (req, res) => {
|
2026-03-12 12:47:56 +08:00
|
|
|
|
try {
|
2026-03-13 13:06:46 +08:00
|
|
|
|
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
|
|
|
|
}
|
2026-03-13 13:06:46 +08:00
|
|
|
|
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) {
|
2026-03-13 13:06:46 +08:00
|
|
|
|
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 });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-13 13:06:46 +08:00
|
|
|
|
router.post('/diag', (req, res) => {
|
2026-03-12 12:47:56 +08:00
|
|
|
|
try {
|
2026-03-13 13:06:46 +08:00
|
|
|
|
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) {
|
2026-03-13 13:06:46 +08:00
|
|
|
|
console.error('[Diag] Error:', error.message);
|
2026-03-12 12:47:56 +08:00
|
|
|
|
res.status(500).json({ success: false, error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-13 13:06:46 +08:00
|
|
|
|
router.post('/direct/query', async (req, res) => {
|
2026-03-12 12:47:56 +08:00
|
|
|
|
try {
|
2026-03-13 13:06:46 +08:00
|
|
|
|
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
|
|
|
|
}
|
2026-03-13 13:06:46 +08:00
|
|
|
|
const context = await db.getHistoryForLLM(sessionId, 20).catch(() => []);
|
|
|
|
|
|
const cleanQuery = (query || '').trim();
|
|
|
|
|
|
if (appendUserMessage && cleanQuery) {
|
|
|
|
|
|
await db.addMessage(sessionId, 'user', cleanQuery, 'voice_asr').catch(() => null);
|
|
|
|
|
|
}
|
|
|
|
|
|
const result = await ToolExecutor.execute('search_knowledge', { query: cleanQuery }, context);
|
|
|
|
|
|
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,
|
2026-03-17 11:00:09 +08:00
|
|
|
|
selected_dataset_ids: result?.selected_dataset_ids || null,
|
|
|
|
|
|
selected_kb_routes: result?.selected_kb_routes || null,
|
2026-03-13 13:06:46 +08:00
|
|
|
|
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) {
|
2026-03-13 13:06:46 +08:00
|
|
|
|
console.error('[DirectVoice] Query failed:', error.message);
|
2026-03-12 12:47:56 +08:00
|
|
|
|
res.status(500).json({ success: false, error: error.message });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-13 13:06:46 +08:00
|
|
|
|
router.post('/direct/stop', async (req, res) => {
|
2026-03-12 12:47:56 +08:00
|
|
|
|
try {
|
2026-03-13 13:06:46 +08:00
|
|
|
|
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
|
|
|
|
}
|
2026-03-13 13:06:46 +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
|
|
|
|
});
|
2026-03-13 13:06:46 +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;
|