llm-chat/backend/routes/chat.js

376 lines
8.9 KiB
JavaScript
Raw Permalink Normal View History

const express = require('express');
const ChatService = require('../services/ChatService');
const router = express.Router();
const chatService = new ChatService();
/**
* 发送消息
* POST /api/chat/send
*/
router.post('/send', async (req, res) => {
try {
const { conversationId, message, options = {} } = req.body;
// 验证必需参数
if (!conversationId || isNaN(conversationId)) {
return res.status(400).json({
success: false,
error: '无效的对话ID',
message: '参数错误'
});
}
if (!message || message.trim().length === 0) {
return res.status(400).json({
success: false,
error: '消息内容不能为空',
message: '参数错误'
});
}
// 限制消息长度
if (message.length > 10000) {
return res.status(400).json({
success: false,
error: '消息长度不能超过10000个字符',
message: '参数错误'
});
}
// 处理消息
const result = await chatService.processMessage(
parseInt(conversationId),
message.trim(),
options
);
res.json({
success: true,
data: result,
message: '消息发送成功'
});
} catch (error) {
console.error('发送消息API错误:', error);
// 根据错误类型返回不同的HTTP状态码
let statusCode = 500;
let errorMessage = '发送消息失败';
if (error.message === '对话不存在') {
statusCode = 404;
errorMessage = '对话未找到';
} else if (error.message.includes('AI服务配置错误')) {
statusCode = 503;
errorMessage = 'AI服务配置错误';
} else if (error.message.includes('请求过于频繁')) {
statusCode = 429;
errorMessage = '请求过于频繁';
} else if (error.message.includes('网络')) {
statusCode = 503;
errorMessage = '网络连接异常';
}
res.status(statusCode).json({
success: false,
error: error.message,
message: errorMessage
});
}
});
/**
* 发送流式消息
* POST /api/chat/stream
*/
router.post('/stream', async (req, res) => {
try {
const { conversationId, message, options = {} } = req.body;
// 验证必需参数
if (!conversationId || isNaN(conversationId)) {
return res.status(400).json({
success: false,
error: '无效的对话ID',
message: '参数错误'
});
}
if (!message || message.trim().length === 0) {
return res.status(400).json({
success: false,
error: '消息内容不能为空',
message: '参数错误'
});
}
// 设置流式响应头
res.writeHead(200, {
'Content-Type': 'text/plain; charset=utf-8',
'Transfer-Encoding': 'chunked',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
const streamResult = await chatService.processStreamMessage(
parseInt(conversationId),
message.trim(),
options
);
// 发送用户消息信息
res.write(JSON.stringify({
type: 'user_message',
data: streamResult.userMessage
}) + '\n');
let fullContent = '';
// 处理流式响应
streamResult.stream.on('data', (chunk) => {
const lines = chunk.toString().split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') {
// 流式响应结束
res.write(JSON.stringify({
type: 'done',
data: { content: fullContent }
}) + '\n');
res.end();
// 保存完整的AI回复
chatService.completeStreamMessage(conversationId, fullContent)
.catch(error => {
console.error('保存流式消息失败:', error);
});
return;
}
try {
const parsed = JSON.parse(data);
const delta = parsed.choices?.[0]?.delta;
if (delta?.content) {
fullContent += delta.content;
// 发送增量内容
res.write(JSON.stringify({
type: 'content',
data: { delta: delta.content, content: fullContent }
}) + '\n');
}
} catch (parseError) {
console.error('解析流式数据失败:', parseError);
}
}
}
});
streamResult.stream.on('error', (error) => {
console.error('流式响应错误:', error);
res.write(JSON.stringify({
type: 'error',
data: { error: error.message }
}) + '\n');
res.end();
});
} catch (error) {
console.error('流式消息API错误:', error);
if (!res.headersSent) {
res.status(500).json({
success: false,
error: error.message,
message: '流式消息发送失败'
});
} else {
res.write(JSON.stringify({
type: 'error',
data: { error: error.message }
}) + '\n');
res.end();
}
}
});
/**
* 重新生成回复
* POST /api/chat/regenerate
*/
router.post('/regenerate', async (req, res) => {
try {
const { conversationId, options = {} } = req.body;
if (!conversationId || isNaN(conversationId)) {
return res.status(400).json({
success: false,
error: '无效的对话ID',
message: '参数错误'
});
}
const result = await chatService.regenerateResponse(
parseInt(conversationId),
options
);
res.json({
success: true,
data: result,
message: '重新生成回复成功'
});
} catch (error) {
console.error('重新生成回复API错误:', error);
let statusCode = 500;
let errorMessage = '重新生成回复失败';
if (error.message === '对话不存在') {
statusCode = 404;
errorMessage = '对话未找到';
} else if (error.message === '没有找到用户消息') {
statusCode = 400;
errorMessage = '没有可重新生成的消息';
}
res.status(statusCode).json({
success: false,
error: error.message,
message: errorMessage
});
}
});
/**
* 检查LLM服务状态
* GET /api/chat/status
*/
router.get('/status', async (req, res) => {
try {
const status = await chatService.checkLLMStatus();
res.json({
success: true,
data: status,
message: 'LLM服务状态检查完成'
});
} catch (error) {
console.error('检查LLM状态API错误:', error);
res.status(500).json({
success: false,
error: error.message,
message: '检查LLM状态失败'
});
}
});
/**
* 获取聊天统计信息
* GET /api/chat/stats
*/
router.get('/stats', async (req, res) => {
try {
const stats = await chatService.getChatStats();
res.json({
success: true,
data: stats,
message: '获取聊天统计成功'
});
} catch (error) {
console.error('获取聊天统计API错误:', error);
res.status(500).json({
success: false,
error: error.message,
message: '获取聊天统计失败'
});
}
});
/**
* 清除缓存
* POST /api/chat/clear-cache
*/
router.post('/clear-cache', async (req, res) => {
try {
const { type = 'all' } = req.body;
const cacheManager = require('../utils/cache');
if (type === 'all') {
cacheManager.clearAll();
} else {
cacheManager.clear(type);
}
res.json({
success: true,
message: `${type}缓存清理成功`
});
} catch (error) {
console.error('清除缓存API错误:', error);
res.status(500).json({
success: false,
error: error.message,
message: '清除缓存失败'
});
}
});
/**
* 获取对话上下文
* GET /api/chat/context/:conversationId
*/
router.get('/context/:conversationId', async (req, res) => {
try {
const { conversationId } = req.params;
const { maxTokens = 4000 } = req.query;
if (!conversationId || isNaN(conversationId)) {
return res.status(400).json({
success: false,
error: '无效的对话ID',
message: '参数错误'
});
}
const context = await chatService.getConversationContext(
parseInt(conversationId),
parseInt(maxTokens)
);
res.json({
success: true,
data: {
context,
messageCount: context.length,
estimatedTokens: context.reduce((sum, msg) =>
sum + (msg.content?.length || 0), 0
)
},
message: '获取对话上下文成功'
});
} catch (error) {
console.error('获取对话上下文API错误:', error);
res.status(500).json({
success: false,
error: error.message,
message: '获取对话上下文失败'
});
}
});
module.exports = router;