fix: clinicId on all appointment paths + startedAt on call records

- AI chat book_appointment tool: accepts optional clinicId
- Widget booking: passes clinicId from request
- LiveKit agent: passes clinicId from doctor context if available
- Dispose endpoint: sets startedAt/endedAt on outbound call records
  (computed from durationSec). Fixes null timestamps in call history.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 14:52:30 +05:30
parent a4ff052fef
commit b6b597fdda
5 changed files with 16 additions and 4 deletions

View File

@@ -380,17 +380,18 @@ export class AiChatController {
}), }),
book_appointment: tool({ book_appointment: tool({
description: 'Book an appointment for a patient. Collect patient name, phone, department, doctor, preferred date/time, and reason before calling this.', description: 'Book an appointment for a patient. Collect patient name, phone, department, doctor, clinic/branch, preferred date/time, and reason before calling this.',
inputSchema: z.object({ inputSchema: z.object({
patientName: z.string().describe('Full name of the patient'), patientName: z.string().describe('Full name of the patient'),
phoneNumber: z.string().describe('Patient phone number'), phoneNumber: z.string().describe('Patient phone number'),
department: z.string().describe('Department for the appointment'), department: z.string().describe('Department for the appointment'),
doctorName: z.string().describe('Doctor name'), doctorName: z.string().describe('Doctor name'),
clinicId: z.string().optional().describe('Clinic/branch ID — get from lookup_doctor results'),
scheduledAt: z.string().describe('Date and time in ISO format (e.g. 2026-04-01T10:00:00)'), scheduledAt: z.string().describe('Date and time in ISO format (e.g. 2026-04-01T10:00:00)'),
reason: z.string().describe('Reason for visit'), reason: z.string().describe('Reason for visit'),
}), }),
execute: async ({ patientName, phoneNumber, department, doctorName, scheduledAt, reason }) => { execute: async ({ patientName, phoneNumber, department, doctorName, clinicId, scheduledAt, reason }) => {
this.logger.log(`[TOOL] book_appointment: ${patientName} | ${phoneNumber} | ${department} | ${doctorName} | ${scheduledAt}`); this.logger.log(`[TOOL] book_appointment: ${patientName} | ${phoneNumber} | ${department} | ${doctorName} | clinic=${clinicId ?? 'none'} | ${scheduledAt}`);
try { try {
const result = await platformService.queryWithAuth<any>( const result = await platformService.queryWithAuth<any>(
`mutation($data: AppointmentCreateInput!) { createAppointment(data: $data) { id } }`, `mutation($data: AppointmentCreateInput!) { createAppointment(data: $data) { id } }`,
@@ -402,6 +403,7 @@ export class AiChatController {
doctorName, doctorName,
department, department,
reasonForVisit: reason, reasonForVisit: reason,
...(clinicId ? { clinicId } : {}),
}, },
}, },
auth, auth,

View File

@@ -128,6 +128,7 @@ const bookAppointment = llm.tool({
doctorName: doctor?.name ?? doctorName ?? 'To be assigned', doctorName: doctor?.name ?? doctorName ?? 'To be assigned',
department, department,
reasonForVisit: reason, reasonForVisit: reason,
...((doctor as any)?.clinicId ? { clinicId: (doctor as any).clinicId } : {}),
}, },
}, },
); );

View File

@@ -147,14 +147,21 @@ export class OzonetelAgentController {
// only place that creates the call record for outbound dials. // only place that creates the call record for outbound dials.
if (body.direction === 'OUTBOUND' && body.callerPhone) { if (body.direction === 'OUTBOUND' && body.callerPhone) {
try { try {
const durationSec = body.durationSec ?? 0;
const endedAt = new Date().toISOString();
const startedAt = durationSec > 0
? new Date(Date.now() - durationSec * 1000).toISOString()
: endedAt;
const callData: Record<string, any> = { const callData: Record<string, any> = {
name: `Outbound — ${body.callerPhone}`, name: `Outbound — ${body.callerPhone}`,
direction: 'OUTBOUND', direction: 'OUTBOUND',
callStatus: 'COMPLETED', callStatus: 'COMPLETED',
callerNumber: { primaryPhoneNumber: `+91${body.callerPhone.replace(/^\+?91/, '')}` }, callerNumber: { primaryPhoneNumber: `+91${body.callerPhone.replace(/^\+?91/, '')}` },
agentName: agentId, agentName: agentId,
durationSec: body.durationSec ?? 0, durationSec,
disposition: body.disposition, disposition: body.disposition,
startedAt,
endedAt,
}; };
if (body.leadId) callData.leadId = body.leadId; if (body.leadId) callData.leadId = body.leadId;

View File

@@ -204,6 +204,7 @@ export class WidgetService {
department: req.departmentId, department: req.departmentId,
reasonForVisit: req.chiefComplaint ?? '', reasonForVisit: req.chiefComplaint ?? '',
patientId, patientId,
...(req.clinicId ? { clinicId: req.clinicId } : {}),
} }, } },
this.auth, this.auth,
); );

View File

@@ -15,6 +15,7 @@ export type WidgetInitResponse = {
export type WidgetBookRequest = { export type WidgetBookRequest = {
departmentId: string; departmentId: string;
doctorId: string; doctorId: string;
clinicId?: string;
scheduledAt: string; scheduledAt: string;
patientName: string; patientName: string;
patientPhone: string; patientPhone: string;