first commit
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user