156 lines
6.3 KiB
JavaScript
156 lines
6.3 KiB
JavaScript
const groqService = require('./groqService');
|
|
const model1Service = require('./model1Service');
|
|
const logger = require('../utils/logger');
|
|
|
|
class ChatRouter {
|
|
constructor() {
|
|
this.systemPrompt = `You are ReasonAI, a specialized engineering assistant. Your role is to:
|
|
|
|
1. **Identify Engineering Questions**: Determine if a user's message requires an engineering plan or is a simple question/conversation.
|
|
|
|
2. **Respond Appropriately**:
|
|
- For engineering questions: Indicate that a plan should be generated
|
|
- For simple questions/greetings: Provide helpful responses within your engineering scope
|
|
- For conversation context: Use conversation history to provide relevant responses
|
|
- For out-of-scope questions: Politely redirect to engineering topics
|
|
|
|
3. **Scope Boundaries**:
|
|
- ✅ Engineering: Civil, mechanical, electrical, chemical, environmental, software engineering
|
|
- ✅ Technical: Calculations, design, analysis, standards, codes, materials
|
|
- ✅ Simple: Greetings, basic engineering concepts, "what is 2+2"
|
|
- ✅ Conversation: Questions about previous messages, context, follow-ups
|
|
- ❌ Out-of-scope: Sports, entertainment, politics, general knowledge outside engineering
|
|
|
|
4. **Context Awareness**: Use conversation history to provide relevant responses. If user asks about previous messages or context, respond helpfully.
|
|
|
|
5. Some questiosn might be engineering questions but not every questions requires a plan, if it can be answered straightforwardly, then it doesnt need a plan
|
|
|
|
6. **Response Format (STRICT)**: Respond ONLY with a single, minified JSON object. No prose, no code fences, no backticks, no explanations. Use DOUBLE QUOTES for all keys and string values. If unsure, default to simple_response. The JSON schema is:
|
|
{
|
|
"isEngineeringQuestion": boolean,
|
|
"responseType": "plan_needed" | "simple_response" | "out_of_scope",
|
|
"response": "Your response to the user",
|
|
"reasoning": "Why you classified it this way"
|
|
}`;
|
|
}
|
|
|
|
async routeMessage(content, context = {}) {
|
|
try {
|
|
const messages = [
|
|
{
|
|
role: 'system',
|
|
content: this.systemPrompt
|
|
}
|
|
];
|
|
|
|
// Add conversation history for context
|
|
if (context.conversationHistory && context.conversationHistory.length > 0) {
|
|
context.conversationHistory.forEach(msg => {
|
|
messages.push({
|
|
role: msg.role,
|
|
content: msg.content
|
|
});
|
|
});
|
|
}
|
|
|
|
// Add current message
|
|
messages.push({
|
|
role: 'user',
|
|
content: `Current user message: "${content}"\n\nContext: ${JSON.stringify(context)}\n\nConversation History: ${context.conversationHistory ? JSON.stringify(context.conversationHistory) : 'None'}`
|
|
});
|
|
|
|
const response = await groqService.generateResponse(messages, {
|
|
temperature: 0.3,
|
|
max_tokens: 4000
|
|
});
|
|
|
|
// Parse the JSON response
|
|
let routingDecision;
|
|
try {
|
|
// Prefer fenced json block if present
|
|
const fence = response.content.match(/```json\s*([\s\S]*?)```/i);
|
|
const raw = fence ? fence[1] : (response.content.match(/\{[\s\S]*\}/) || [null])[0];
|
|
if (!raw) throw new Error('No JSON found in response');
|
|
|
|
// Trim whitespace and attempt strict parse
|
|
const candidate = raw.trim();
|
|
routingDecision = JSON.parse(candidate);
|
|
} catch (parseError) {
|
|
// Downgrade to warn to avoid noisy error logs for non-strict JSON
|
|
logger.warn('Routing JSON parse failed; falling back to heuristic classification');
|
|
|
|
// Smart fallback based on content analysis
|
|
const lowerContent = content.toLowerCase();
|
|
const engineeringKeywords = ['design', 'calculate', 'analyze', 'beam', 'steel', 'concrete', 'structure', 'load', 'stress', 'engineering', 'technical', 'specification', 'code', 'standard'];
|
|
const isEngineering = engineeringKeywords.some(keyword => lowerContent.includes(keyword));
|
|
|
|
routingDecision = {
|
|
isEngineeringQuestion: isEngineering,
|
|
responseType: isEngineering ? 'plan_needed' : 'simple_response',
|
|
response: isEngineering ?
|
|
"I'll generate an engineering plan for your request." :
|
|
"I'm ReasonAI, your engineering assistant. I can help with engineering questions, calculations, and technical problems. How can I assist you today?",
|
|
reasoning: "Fallback routing due to parse error"
|
|
};
|
|
}
|
|
|
|
logger.info(`Message routed as: ${routingDecision.responseType}`, {
|
|
content: content.substring(0, 100),
|
|
reasoning: routingDecision.reasoning,
|
|
conversationHistoryLength: context.conversationHistory ? context.conversationHistory.length : 0
|
|
});
|
|
|
|
return routingDecision;
|
|
} catch (error) {
|
|
logger.error('Chat routing error:', error);
|
|
|
|
// Fallback response
|
|
return {
|
|
isEngineeringQuestion: false,
|
|
responseType: 'simple_response',
|
|
response: "I'm ReasonAI, your engineering assistant. I'm here to help with engineering questions and technical problems. How can I assist you today?",
|
|
reasoning: "Routing service error"
|
|
};
|
|
}
|
|
}
|
|
|
|
async generateSimpleResponse(content, context = {}) {
|
|
try {
|
|
const messages = [
|
|
{
|
|
role: 'system',
|
|
content: `You are ReasonAI, a friendly engineering assistant. Respond helpfully to the user's message while staying within your engineering scope. Be concise and professional. Consider the conversation history for context.`
|
|
}
|
|
];
|
|
|
|
// Add conversation history for context
|
|
if (context.conversationHistory && context.conversationHistory.length > 0) {
|
|
context.conversationHistory.forEach(msg => {
|
|
messages.push({
|
|
role: msg.role,
|
|
content: msg.content
|
|
});
|
|
});
|
|
}
|
|
|
|
// Add current message
|
|
messages.push({
|
|
role: 'user',
|
|
content: content
|
|
});
|
|
|
|
const response = await groqService.generateResponse(messages, {
|
|
temperature: 0.7,
|
|
max_tokens: 300
|
|
});
|
|
|
|
return response.content;
|
|
} catch (error) {
|
|
logger.error('Simple response generation error:', error);
|
|
return "I'm ReasonAI, your engineering assistant. I can help with engineering questions, calculations, and technical problems. How can I assist you today?";
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = new ChatRouter();
|