356 lines
9.4 KiB
JavaScript
356 lines
9.4 KiB
JavaScript
|
|
const { Conversation, Message, Plan, User } = require('../models');
|
||
|
|
const logger = require('../utils/logger');
|
||
|
|
const model1Service = require('../services/model1Service');
|
||
|
|
const chatRouter = require('../services/chatRouter');
|
||
|
|
|
||
|
|
const createConversation = async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { title } = req.body;
|
||
|
|
const userId = req.user.userId;
|
||
|
|
|
||
|
|
const conversation = await Conversation.create({
|
||
|
|
user_id: userId,
|
||
|
|
title: title || 'New Conversation',
|
||
|
|
status: 'active'
|
||
|
|
});
|
||
|
|
|
||
|
|
logger.info(`New conversation created: ${conversation.id}`);
|
||
|
|
|
||
|
|
res.status(201).json({
|
||
|
|
success: true,
|
||
|
|
data: { conversation }
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
logger.error('Create conversation error:', error);
|
||
|
|
res.status(500).json({
|
||
|
|
success: false,
|
||
|
|
error: 'Internal server error'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const getConversations = async (req, res) => {
|
||
|
|
try {
|
||
|
|
const userId = req.user.userId;
|
||
|
|
const { page = 1, limit = 10, status } = req.query;
|
||
|
|
|
||
|
|
const whereClause = { user_id: userId };
|
||
|
|
if (status) whereClause.status = status;
|
||
|
|
|
||
|
|
const conversations = await Conversation.findAndCountAll({
|
||
|
|
where: whereClause,
|
||
|
|
include: [
|
||
|
|
{
|
||
|
|
model: Message,
|
||
|
|
limit: 1,
|
||
|
|
order: [['created_at', 'DESC']]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
order: [['created_at', 'DESC']],
|
||
|
|
limit: parseInt(limit),
|
||
|
|
offset: (parseInt(page) - 1) * parseInt(limit)
|
||
|
|
});
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: {
|
||
|
|
conversations: conversations.rows,
|
||
|
|
pagination: {
|
||
|
|
page: parseInt(page),
|
||
|
|
limit: parseInt(limit),
|
||
|
|
total: conversations.count,
|
||
|
|
pages: Math.ceil(conversations.count / limit)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
logger.error('Get conversations error:', error);
|
||
|
|
res.status(500).json({
|
||
|
|
success: false,
|
||
|
|
error: 'Internal server error'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const getConversation = async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { conversationId } = req.params;
|
||
|
|
const userId = req.user.userId;
|
||
|
|
|
||
|
|
const conversation = await Conversation.findOne({
|
||
|
|
where: {
|
||
|
|
id: conversationId,
|
||
|
|
user_id: userId
|
||
|
|
},
|
||
|
|
include: [
|
||
|
|
{
|
||
|
|
model: Message,
|
||
|
|
order: [['created_at', 'ASC']]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
model: Plan,
|
||
|
|
order: [['created_at', 'DESC']]
|
||
|
|
}
|
||
|
|
]
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!conversation) {
|
||
|
|
return res.status(404).json({
|
||
|
|
success: false,
|
||
|
|
error: 'Conversation not found'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: { conversation }
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
logger.error('Get conversation error:', error);
|
||
|
|
res.status(500).json({
|
||
|
|
success: false,
|
||
|
|
error: 'Internal server error'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const sendMessage = async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { conversationId, content, messageType = 'text' } = req.body;
|
||
|
|
const userId = req.user.userId;
|
||
|
|
|
||
|
|
// Verify conversation belongs to user
|
||
|
|
const conversation = await Conversation.findOne({
|
||
|
|
where: {
|
||
|
|
id: conversationId,
|
||
|
|
user_id: userId
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!conversation) {
|
||
|
|
return res.status(404).json({
|
||
|
|
success: false,
|
||
|
|
error: 'Conversation not found'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Create user message
|
||
|
|
const userMessage = await Message.create({
|
||
|
|
conversation_id: conversationId,
|
||
|
|
role: 'user',
|
||
|
|
content,
|
||
|
|
message_type: messageType
|
||
|
|
});
|
||
|
|
|
||
|
|
// Get conversation history for context
|
||
|
|
const conversationHistory = await Message.findAll({
|
||
|
|
where: { conversation_id: conversationId },
|
||
|
|
order: [['created_at', 'DESC']],
|
||
|
|
limit: 20 // Last 20 messages for context
|
||
|
|
});
|
||
|
|
|
||
|
|
// Create properly ordered conversation history for context
|
||
|
|
const orderedHistory = conversationHistory.reverse().map(msg => ({
|
||
|
|
role: msg.role,
|
||
|
|
content: msg.content,
|
||
|
|
message_type: msg.message_type,
|
||
|
|
timestamp: msg.created_at
|
||
|
|
}));
|
||
|
|
|
||
|
|
// Route the message to determine response type
|
||
|
|
const routingDecision = await chatRouter.routeMessage(content, {
|
||
|
|
conversationId,
|
||
|
|
userId,
|
||
|
|
timestamp: new Date().toISOString(),
|
||
|
|
conversationHistory: orderedHistory
|
||
|
|
});
|
||
|
|
|
||
|
|
let assistantMessage;
|
||
|
|
|
||
|
|
if (routingDecision.responseType === 'plan_needed' && routingDecision.isEngineeringQuestion) {
|
||
|
|
// Generate engineering plan using MODEL1
|
||
|
|
try {
|
||
|
|
const planData = await model1Service.generatePlan(content, {
|
||
|
|
conversationId,
|
||
|
|
userId,
|
||
|
|
timestamp: new Date().toISOString()
|
||
|
|
});
|
||
|
|
|
||
|
|
// Save the plan to database
|
||
|
|
const plan = await Plan.create({
|
||
|
|
conversation_id: conversationId,
|
||
|
|
title: planData.title,
|
||
|
|
description: planData.description,
|
||
|
|
steps: planData.steps,
|
||
|
|
status: 'draft',
|
||
|
|
tools_required: planData.toolsRequired || [],
|
||
|
|
estimated_duration: planData.estimatedDuration,
|
||
|
|
complexity_score: planData.complexityScore,
|
||
|
|
metadata: {
|
||
|
|
safetyConsiderations: planData.safetyConsiderations,
|
||
|
|
qualityChecks: planData.qualityChecks,
|
||
|
|
processingTime: planData.processingTime,
|
||
|
|
tokensUsed: planData.tokensUsed,
|
||
|
|
model: planData.model
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Create assistant message with plan
|
||
|
|
assistantMessage = await Message.create({
|
||
|
|
conversation_id: conversationId,
|
||
|
|
plan_id: plan.id,
|
||
|
|
role: 'assistant',
|
||
|
|
content: planData.rawContent || `# ${planData.title}\n\n${planData.description}\n\n## Steps:\n${planData.steps.map((step, index) => `${index + 1}. ${step}`).join('\n')}`,
|
||
|
|
message_type: 'plan',
|
||
|
|
metadata: {
|
||
|
|
modelType: 'MODEL1',
|
||
|
|
processingTime: planData.processingTime,
|
||
|
|
tokensUsed: planData.tokensUsed,
|
||
|
|
routingDecision: routingDecision.reasoning
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
logger.info(`MODEL1 plan generated: ${plan.id} for conversation: ${conversationId}`);
|
||
|
|
} catch (error) {
|
||
|
|
logger.error('MODEL1 plan generation failed:', error);
|
||
|
|
|
||
|
|
// Fallback to simple response if plan generation fails
|
||
|
|
const fallbackResponse = await chatRouter.generateSimpleResponse(content, {
|
||
|
|
conversationHistory: orderedHistory
|
||
|
|
});
|
||
|
|
assistantMessage = await Message.create({
|
||
|
|
conversation_id: conversationId,
|
||
|
|
role: 'assistant',
|
||
|
|
content: fallbackResponse,
|
||
|
|
message_type: 'text',
|
||
|
|
metadata: {
|
||
|
|
error: error.message,
|
||
|
|
modelType: 'FALLBACK',
|
||
|
|
routingDecision: routingDecision.reasoning
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// Generate simple conversational response
|
||
|
|
const responseContent = routingDecision.response || await chatRouter.generateSimpleResponse(content, {
|
||
|
|
conversationHistory: orderedHistory
|
||
|
|
});
|
||
|
|
|
||
|
|
assistantMessage = await Message.create({
|
||
|
|
conversation_id: conversationId,
|
||
|
|
role: 'assistant',
|
||
|
|
content: responseContent,
|
||
|
|
message_type: 'text',
|
||
|
|
metadata: {
|
||
|
|
modelType: 'REASONAI',
|
||
|
|
routingDecision: routingDecision.reasoning,
|
||
|
|
responseType: routingDecision.responseType
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
logger.info(`Simple response generated for conversation: ${conversationId}`, {
|
||
|
|
responseType: routingDecision.responseType,
|
||
|
|
reasoning: routingDecision.reasoning
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
logger.info(`Message sent in conversation: ${conversationId}`);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: { userMessage, assistantMessage }
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
logger.error('Send message error:', error);
|
||
|
|
res.status(500).json({
|
||
|
|
success: false,
|
||
|
|
error: 'Internal server error'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const updateConversation = async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { conversationId } = req.params;
|
||
|
|
const { title, status } = req.body;
|
||
|
|
const userId = req.user.userId;
|
||
|
|
|
||
|
|
const conversation = await Conversation.findOne({
|
||
|
|
where: {
|
||
|
|
id: conversationId,
|
||
|
|
user_id: userId
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!conversation) {
|
||
|
|
return res.status(404).json({
|
||
|
|
success: false,
|
||
|
|
error: 'Conversation not found'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
const updateData = {};
|
||
|
|
if (title) updateData.title = title;
|
||
|
|
if (status) updateData.status = status;
|
||
|
|
|
||
|
|
await conversation.update(updateData);
|
||
|
|
|
||
|
|
logger.info(`Conversation updated: ${conversationId}`);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: { conversation }
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
logger.error('Update conversation error:', error);
|
||
|
|
res.status(500).json({
|
||
|
|
success: false,
|
||
|
|
error: 'Internal server error'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const deleteConversation = async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { conversationId } = req.params;
|
||
|
|
const userId = req.user.userId;
|
||
|
|
|
||
|
|
const conversation = await Conversation.findOne({
|
||
|
|
where: {
|
||
|
|
id: conversationId,
|
||
|
|
user_id: userId
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!conversation) {
|
||
|
|
return res.status(404).json({
|
||
|
|
success: false,
|
||
|
|
error: 'Conversation not found'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
await conversation.destroy();
|
||
|
|
|
||
|
|
logger.info(`Conversation deleted: ${conversationId}`);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
message: 'Conversation deleted successfully'
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
logger.error('Delete conversation error:', error);
|
||
|
|
res.status(500).json({
|
||
|
|
success: false,
|
||
|
|
error: 'Internal server error'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
createConversation,
|
||
|
|
getConversations,
|
||
|
|
getConversation,
|
||
|
|
sendMessage,
|
||
|
|
updateConversation,
|
||
|
|
deleteConversation
|
||
|
|
};
|