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

168 lines
6.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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');
const directSessions = new Map();
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) => {
try {
const { userId, sessionId } = req.body || {};
const sid = sessionId || uuidv4();
const directSession = {
sessionId: sid,
userId: userId || null,
startTime: Date.now(),
direct: true,
};
directSessions.set(sid, directSession);
await db.createSession(sid, userId || null, 'voice');
res.json({
success: true,
data: {
sessionId: sid,
userId: userId || null,
},
});
} catch (error) {
console.error('[DirectVoice] Create session failed:', error.message);
res.status(500).json({ success: false, error: error.message });
}
});
router.post('/direct/message', async (req, res) => {
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' });
}
await db.addMessage(sessionId, role === 'user' ? 'user' : 'assistant', text, source, toolName || null);
res.json({ success: true });
} catch (error) {
console.error('[DirectVoice] Add message failed:', error.message);
res.status(500).json({ success: false, error: error.message });
}
});
router.post('/diag', (req, res) => {
try {
const { sessionId, roomId, type, payload } = req.body || {};
console.log(`[Diag] type=${type || 'unknown'} session=${sessionId || '-'} room=${roomId || '-'} payload=${JSON.stringify(payload || {})}`);
res.json({ success: true });
} catch (error) {
console.error('[Diag] Error:', error.message);
res.status(500).json({ success: false, error: error.message });
}
});
router.post('/direct/query', async (req, res) => {
try {
const { sessionId, query, appendUserMessage } = req.body || {};
if (!sessionId) {
return res.status(400).json({ success: false, error: 'sessionId is required' });
}
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,
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),
},
});
} catch (error) {
console.error('[DirectVoice] Query failed:', error.message);
res.status(500).json({ success: false, error: error.message });
}
});
router.post('/direct/stop', async (req, res) => {
try {
const { sessionId } = req.body || {};
if (!sessionId) {
return res.status(400).json({ success: false, error: 'sessionId is required' });
}
directSessions.delete(sessionId);
const messages = await db.getMessages(sessionId).catch(() => []);
res.json({
success: true,
data: {
sessionId,
messageCount: messages.length,
},
});
} catch (error) {
console.error('[DirectVoice] Stop session failed:', error.message);
res.status(500).json({ success: false, error: error.message });
}
});
module.exports = router;