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();