diff --git a/src/ai/ai-chat.controller.ts b/src/ai/ai-chat.controller.ts index 39bc96b..c96738d 100644 --- a/src/ai/ai-chat.controller.ts +++ b/src/ai/ai-chat.controller.ts @@ -82,6 +82,9 @@ export class AiChatController { private async buildKnowledgeBase(auth: string): Promise { const now = Date.now(); if (this.knowledgeBase && now - this.kbLoadedAt < this.kbTtlMs) { + this.logger.log( + `KB cache hit (${this.knowledgeBase.length} chars, age ${Math.round((now - this.kbLoadedAt) / 1000)}s)`, + ); return this.knowledgeBase; } @@ -150,6 +153,7 @@ export class AiChatController { } } catch (err) { this.logger.warn(`Failed to fetch clinics: ${err}`); + sections.push('## Clinics\nFailed to load clinic data.'); } try { @@ -188,6 +192,7 @@ export class AiChatController { } } catch (err) { this.logger.warn(`Failed to fetch health packages: ${err}`); + sections.push('\n## Health Packages\nFailed to load package data.'); } try { @@ -211,6 +216,7 @@ export class AiChatController { } } catch (err) { this.logger.warn(`Failed to fetch insurance partners: ${err}`); + sections.push('\n## Insurance Partners\nFailed to load insurance data.'); } this.knowledgeBase = @@ -241,7 +247,11 @@ ${kb}`; private async chatWithTools(userMessage: string, auth: string) { const kb = await this.buildKnowledgeBase(auth); + this.logger.log(`KB content preview: ${kb.substring(0, 300)}...`); const systemPrompt = this.buildSystemPrompt(kb); + this.logger.log( + `System prompt length: ${systemPrompt.length} chars, user message: "${userMessage.substring(0, 100)}"`, + ); const platformService = this.platform; const { text, steps } = await generateText({ diff --git a/src/call-assist/call-assist.service.ts b/src/call-assist/call-assist.service.ts index 34d8127..0a89f57 100644 --- a/src/call-assist/call-assist.service.ts +++ b/src/call-assist/call-assist.service.ts @@ -62,7 +62,7 @@ export class CallAssistService { const apptResult = await this.platform.queryWithAuth( `{ appointments(first: 10, orderBy: [{ scheduledAt: DescNullsLast }]) { edges { node { - id scheduledAt appointmentStatus doctorName department reasonForVisit patientId + id scheduledAt status doctorName department reasonForVisit patientId } } } }`, undefined, authHeader, @@ -77,7 +77,7 @@ export class CallAssistService { ? new Date(a.scheduledAt).toLocaleDateString('en-IN') : '?'; parts.push( - `- ${date}: ${a.doctorName ?? '?'} (${a.department ?? '?'}) — ${a.appointmentStatus}`, + `- ${date}: ${a.doctorName ?? '?'} (${a.department ?? '?'}) — ${a.status}`, ); } } diff --git a/src/search/search.controller.ts b/src/search/search.controller.ts index acfe6f4..4795995 100644 --- a/src/search/search.controller.ts +++ b/src/search/search.controller.ts @@ -60,7 +60,7 @@ export class SearchController { this.platform .queryWithAuth( `{ appointments(first: 50, orderBy: [{ scheduledAt: DescNullsLast }]) { edges { node { - id scheduledAt doctorName department appointmentStatus patientId + id scheduledAt doctorName department status patientId } } } }`, undefined, authHeader, diff --git a/src/worklist/missed-call-webhook.controller.ts b/src/worklist/missed-call-webhook.controller.ts index 34055da..e3b8860 100644 --- a/src/worklist/missed-call-webhook.controller.ts +++ b/src/worklist/missed-call-webhook.controller.ts @@ -169,6 +169,11 @@ export class MissedCallWebhookController { durationSec: data.duration, disposition: this.mapDisposition(data.disposition), }; + // Set callback tracking fields for missed calls so they appear in the worklist + if (data.callStatus === 'MISSED') { + callData.callbackstatus = 'PENDING_CALLBACK'; + callData.missedcallcount = 1; + } if (data.recordingUrl) { callData.recording = { primaryLinkUrl: data.recordingUrl, diff --git a/src/worklist/missed-queue.service.ts b/src/worklist/missed-queue.service.ts index e43a35a..b97003c 100644 --- a/src/worklist/missed-queue.service.ts +++ b/src/worklist/missed-queue.service.ts @@ -50,15 +50,16 @@ export class MissedQueueService implements OnModuleInit { let created = 0; let updated = 0; + // Ozonetel fromTime/toTime use HH:MM:SS format (time of day, filters within current day) const now = new Date(); const fiveMinAgo = new Date(now.getTime() - 5 * 60 * 1000); - const format = (d: Date) => d.toISOString().replace('T', ' ').slice(0, 19); + const toHHMMSS = (d: Date) => d.toTimeString().slice(0, 8); let abandonCalls: any[]; try { abandonCalls = await this.ozonetel.getAbandonCalls({ - fromTime: format(fiveMinAgo), - toTime: format(now), + fromTime: toHHMMSS(fiveMinAgo), + toTime: toHHMMSS(now), }); } catch (err) { this.logger.warn(`Failed to fetch abandon calls: ${err}`); diff --git a/src/worklist/worklist.service.ts b/src/worklist/worklist.service.ts index c32c540..04857aa 100644 --- a/src/worklist/worklist.service.ts +++ b/src/worklist/worklist.service.ts @@ -89,9 +89,9 @@ export class WorklistService { authHeader: string, ): Promise { try { - // FIFO ordering (AscNullsLast) — oldest first. Filter to active callback statuses only. + // FIFO ordering (AscNullsLast) — oldest first. No agentName filter — missed calls are a shared queue. const data = await this.platform.queryWithAuth( - `{ calls(first: 20, filter: { agentName: { eq: "${agentName}" }, callStatus: { eq: MISSED }, callbackstatus: { in: [PENDING_CALLBACK, CALLBACK_ATTEMPTED] } }, orderBy: [{ startedAt: AscNullsLast }]) { edges { node { + `{ calls(first: 20, filter: { callStatus: { eq: MISSED }, callbackstatus: { in: [PENDING_CALLBACK, CALLBACK_ATTEMPTED] } }, orderBy: [{ startedAt: AscNullsLast }]) { edges { node { id name createdAt direction callStatus agentName callerNumber { primaryPhoneNumber }