feat: add AI recall calling — books existing patients for routine cleanings
Adds a new recall conversation flow (recall-graph.ts) triggered from the Patient Connection "AI Call" button: offers an existing patient a routine exam & cleaning, classifies their yes/no reply, then books directly onto the schedule (Column A, 30-minute slots, real conflict checking against office hours and existing appointments) instead of only capturing a preference for staff to follow up on. Also adds a "Voice call" label in the message thread to distinguish call transcripts from SMS, and updates the Call Templates settings UI with an editable recall greeting. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,7 +19,7 @@ import {
|
||||
} from "../ai/patient-conversation-router";
|
||||
import {
|
||||
getHandoff, getAfterHoursHandoff,
|
||||
getStage, setStage, resetConversation,
|
||||
getStage, setStage, resetConversation, startRecallConversation,
|
||||
setPendingReschedule, getPendingReschedule, clearPendingReschedule,
|
||||
type ConversationStage,
|
||||
} from "../ai/aiHandoffStore";
|
||||
@@ -696,6 +696,7 @@ router.post("/webhook/ai-voice", async (req: Request, res: Response): Promise<an
|
||||
try {
|
||||
const { CallSid, SpeechResult } = req.body as { CallSid?: string; SpeechResult?: string };
|
||||
const patientIdParam = req.query.patientId as string | undefined;
|
||||
const purposeParam = req.query.purpose as string | undefined;
|
||||
if (!CallSid) return res.send(aiVoiceHangup("Sorry, something went wrong. Goodbye."));
|
||||
|
||||
let session = aiCallSessions.get(CallSid);
|
||||
@@ -713,13 +714,17 @@ router.post("/webhook/ai-voice", async (req: Request, res: Response): Promise<an
|
||||
|
||||
const language = patient.preferredLanguage || "English";
|
||||
const locale = voiceLocaleFor(language);
|
||||
const isRecallCall = purposeParam === "recall";
|
||||
|
||||
// Resume an in-progress cross-channel conversation (e.g. mid-reschedule
|
||||
// from a prior text) if one exists; otherwise start a fresh reminder-style
|
||||
// check-in call, matching the default the reminder-SMS batch send uses.
|
||||
// from a prior text) if one exists; otherwise start a fresh check-in call.
|
||||
// A "recall" call (staff-triggered from Patient Connection, offering an
|
||||
// existing patient a routine exam & cleaning) seeds a different stage
|
||||
// than the default reminder-style check-in.
|
||||
const currentStage = await getStage(patient.userId, patient.id);
|
||||
if (currentStage === "initial" || currentStage === "done") {
|
||||
await resetConversation(patient.userId, patient.id);
|
||||
if (isRecallCall) await startRecallConversation(patient.userId, patient.id);
|
||||
else await resetConversation(patient.userId, patient.id);
|
||||
}
|
||||
|
||||
const officeContact = await storage.getOfficeContact(patient.userId);
|
||||
@@ -732,7 +737,7 @@ router.post("/webhook/ai-voice", async (req: Request, res: Response): Promise<an
|
||||
};
|
||||
aiCallSessions.set(CallSid, session);
|
||||
|
||||
const rawCallTemplate = callTemplates.greeting?.trim() ||
|
||||
const rawCallTemplate = (isRecallCall ? callTemplates.recallGreeting?.trim() : callTemplates.greeting?.trim()) ||
|
||||
"Hi {firstName}, this is Lisa, the AI assistant at {officeName}. How can I help you today?";
|
||||
const greeting = rawCallTemplate
|
||||
.replace(/\{firstName\}/g, patient.firstName || "there")
|
||||
|
||||
Reference in New Issue
Block a user