93 lines
3.9 KiB
JavaScript
93 lines
3.9 KiB
JavaScript
/**
|
|
* ClinicAgent: An agentic orchestrator for Curio.
|
|
* It uses natural language understanding to map user requests to hospital tools.
|
|
*/
|
|
|
|
const patientManager = require('./patientManager');
|
|
const queueManager = require('./queueManager');
|
|
const pharmacyAgent = require('./pharmacyAgent');
|
|
const configManager = require('./configManager');
|
|
const { OpenAI } = require('openai');
|
|
|
|
const openai = new OpenAI({
|
|
baseURL: process.env.LLM_BASE_URL || 'http://ollama.ai-core.svc.cluster.local:11434/v1',
|
|
apiKey: process.env.LLM_API_KEY || 'ollama', // Ollama doesn't require a real key
|
|
});
|
|
const LLM_MODEL = process.env.LLM_MODEL || 'qwen2.5:3b';
|
|
|
|
class ClinicAgent {
|
|
constructor() {
|
|
this.systemPrompt = `
|
|
You are 'Curio AI', the assistant for Curio Health.
|
|
Your goal is to help patients book appointments.
|
|
Be extremely concise, friendly, and natural. Do not use markdown.
|
|
To book an appointment, you MUST collect:
|
|
1. Department (e.g. Cardiology, Neurology, General)
|
|
2. Date (Format as YYYY-MM-DD or today/tomorrow)
|
|
3. Time (Format as HH:MM AM/PM)
|
|
|
|
If any of these 3 pieces of information are missing, naturally ask the user for them.
|
|
Do NOT output JSON until you have ALL THREE pieces of information.
|
|
|
|
If you have all three pieces of information, you MUST reply with ONLY a JSON block like this (no other text):
|
|
{"action": "BOOK", "department": "Cardiology", "date": "2026-05-26", "time": "10:00"}
|
|
`;
|
|
}
|
|
|
|
async processMessage(phone, transcript) {
|
|
console.log(`[Agent] Processing for ${phone} via Ollama`);
|
|
|
|
try {
|
|
const response = await openai.chat.completions.create({
|
|
model: LLM_MODEL,
|
|
messages: [
|
|
{ role: 'system', content: this.systemPrompt },
|
|
...transcript
|
|
],
|
|
temperature: 0.2,
|
|
});
|
|
|
|
const replyContent = response.choices[0].message.content.trim();
|
|
|
|
// Try to parse JSON intent
|
|
try {
|
|
// Remove potential markdown code blocks if the LLM wrapped it
|
|
const cleanJson = replyContent.replace(/```json/g, '').replace(/```/g, '').trim();
|
|
const intent = JSON.parse(cleanJson);
|
|
|
|
if (intent.action === 'BOOK') {
|
|
// Extract HH:MM
|
|
let timeVal = "10:00"; // fallback
|
|
const timeMatch = intent.time.match(/(\d{1,2}:\d{2})/);
|
|
if (timeMatch) timeVal = timeMatch[1];
|
|
|
|
// Parse date
|
|
let dateVal = new Date().toISOString().split('T')[0];
|
|
if (intent.date && intent.date.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
|
dateVal = intent.date;
|
|
}
|
|
|
|
// Book the token using Dr. Sharma's Cardiology ("sharma-clinic")
|
|
// In a real system, we'd map intent.department to tenantId
|
|
const result = await queueManager.bookOnline(dateVal, timeVal, phone, 'sharma-clinic');
|
|
if (result.success) {
|
|
return { reply: `Awesome! Your appointment for ${intent.department} is booked. Your token is ${result.token}. You should receive a confirmation message shortly.` };
|
|
} else {
|
|
return { reply: `Sorry, ${result.message}` };
|
|
}
|
|
}
|
|
} catch (e) {
|
|
// Not JSON, just regular conversational text
|
|
return { reply: replyContent };
|
|
}
|
|
|
|
return { reply: replyContent };
|
|
} catch (error) {
|
|
console.error('[ClinicAgent] LLM Error:', error.message);
|
|
return { reply: "I'm having trouble connecting to my AI brain right now. Please try again in a moment." };
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = new ClinicAgent();
|