const express = require('express'); const router = express.Router(); const cozeChatService = require('../services/cozeChatService'); const db = require('../db'); // 存储文字对话的会话状态(sessionId -> session) const chatSessions = new Map(); /** * POST /api/chat/start * 创建文字对话会话,可选传入语音通话的历史字幕 */ router.post('/start', async (req, res) => { const { sessionId, voiceSubtitles = [] } = req.body; if (!sessionId) { return res.status(400).json({ success: false, error: 'sessionId is required' }); } if (!cozeChatService.isConfigured()) { return res.status(500).json({ success: false, error: 'Coze 智能体未配置,请设置 COZE_API_TOKEN 和 COZE_BOT_ID' }); } // 优先从数据库加载完整历史(包含语音通话中的工具结果等) let voiceMessages = []; try { const dbHistory = await db.getHistoryForLLM(sessionId, 20); if (dbHistory.length > 0) { voiceMessages = dbHistory; console.log(`[Chat] Loaded ${dbHistory.length} messages from DB for session ${sessionId}`); } } catch (e) { console.warn('[DB] getHistoryForLLM failed:', e.message); } // 如果数据库没有历史,回退到 voiceSubtitles if (voiceMessages.length === 0 && voiceSubtitles.length > 0) { const recentSubtitles = voiceSubtitles.slice(-10); for (const sub of recentSubtitles) { voiceMessages.push({ role: sub.role === 'user' ? 'user' : 'assistant', content: sub.text, }); } } // 更新数据库会话模式为 chat try { await db.createSession(sessionId, `user_${sessionId.slice(0, 12)}`, 'chat'); } catch (e) {} chatSessions.set(sessionId, { userId: `user_${sessionId.slice(0, 12)}`, conversationId: null, voiceMessages, createdAt: Date.now(), fromVoice: voiceSubtitles.length > 0 || voiceMessages.length > 0, }); console.log(`[Chat] Session started: ${sessionId}, fromVoice: ${voiceSubtitles.length > 0}, voiceMessages: ${voiceMessages.length}`); res.json({ success: true, data: { sessionId, messageCount: voiceMessages.length, fromVoice: voiceSubtitles.length > 0 || voiceMessages.length > 0, }, }); }); /** * POST /api/chat/send * 发送文字消息并获取 Coze 智能体回复(非流式) */ router.post('/send', async (req, res) => { try { const { sessionId, message } = req.body; if (!sessionId || !message) { return res.status(400).json({ success: false, error: 'sessionId and message are required' }); } let session = chatSessions.get(sessionId); // 自动创建会话(如果不存在) if (!session) { session = { userId: `user_${sessionId.slice(0, 12)}`, conversationId: null, voiceMessages: [], createdAt: Date.now(), fromVoice: false, }; chatSessions.set(sessionId, session); } console.log(`[Chat] User(${sessionId}): ${message}`); // 写入数据库:用户消息 db.addMessage(sessionId, 'user', message, 'chat_user').catch(e => console.warn('[DB] addMessage failed:', e.message)); // 首次对话时注入语音历史作为上下文,之后 Coze 自动管理会话历史 const extraMessages = !session.conversationId ? session.voiceMessages : []; const result = await cozeChatService.chat( session.userId, message, session.conversationId, extraMessages ); // 保存 Coze 返回的 conversationId session.conversationId = result.conversationId; console.log(`[Chat] Assistant(${sessionId}): ${result.content?.substring(0, 100)}`); // 写入数据库:AI 回复 if (result.content) { db.addMessage(sessionId, 'assistant', result.content, 'chat_bot').catch(e => console.warn('[DB] addMessage failed:', e.message)); } res.json({ success: true, data: { content: result.content, }, }); } catch (error) { console.error('[Chat] Send failed:', error.message); res.status(500).json({ success: false, error: error.message }); } }); /** * GET /api/chat/history/:sessionId * 获取会话状态 */ router.get('/history/:sessionId', (req, res) => { const session = chatSessions.get(req.params.sessionId); if (!session) { return res.json({ success: true, data: [] }); } res.json({ success: true, data: { conversationId: session.conversationId, fromVoice: session.fromVoice, }, }); }); /** * POST /api/chat/send-stream * 流式发送文字消息(SSE),逐字输出 Coze 智能体回复 */ router.post('/send-stream', async (req, res) => { const { sessionId, message } = req.body; if (!sessionId || !message) { return res.status(400).json({ success: false, error: 'sessionId and message are required' }); } let session = chatSessions.get(sessionId); if (!session) { session = { userId: `user_${sessionId.slice(0, 12)}`, conversationId: null, voiceMessages: [], createdAt: Date.now(), fromVoice: false, }; chatSessions.set(sessionId, session); } console.log(`[Chat][SSE] User(${sessionId}): ${message}`); // 写入数据库:用户消息 db.addMessage(sessionId, 'user', message, 'chat_user').catch(e => console.warn('[DB] addMessage failed:', e.message)); // 设置 SSE 响应头 res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); res.setHeader('X-Accel-Buffering', 'no'); res.flushHeaders(); try { // 首次对话时注入语音历史作为上下文 const extraMessages = !session.conversationId ? session.voiceMessages : []; const result = await cozeChatService.chatStream( session.userId, message, session.conversationId, extraMessages, { onChunk: (text) => { res.write(`data: ${JSON.stringify({ type: 'chunk', content: text })}\n\n`); }, onDone: () => {}, } ); // 保存 Coze 返回的 conversationId session.conversationId = result.conversationId; console.log(`[Chat][SSE] Assistant(${sessionId}): ${result.content?.substring(0, 100)}`); // 写入数据库:AI 回复 if (result.content) { db.addMessage(sessionId, 'assistant', result.content, 'chat_bot').catch(e => console.warn('[DB] addMessage failed:', e.message)); } res.write(`data: ${JSON.stringify({ type: 'done', content: result.content })}\n\n`); res.end(); } catch (error) { console.error('[Chat][SSE] Stream failed:', error.message); res.write(`data: ${JSON.stringify({ type: 'error', error: error.message })}\n\n`); res.end(); } }); /** * DELETE /api/chat/:sessionId * 删除对话会话 */ router.delete('/:sessionId', (req, res) => { chatSessions.delete(req.params.sessionId); res.json({ success: true }); }); // 定时清理过期会话(30 分钟无活动) setInterval(() => { const now = Date.now(); const TTL = 30 * 60 * 1000; for (const [id, session] of chatSessions) { if (now - session.createdAt > TTL) { chatSessions.delete(id); console.log(`[Chat] Session expired and cleaned: ${id}`); } } }, 5 * 60 * 1000); module.exports = router;