mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-04-11 18:08:16 +00:00
Merge branch 'dev' into dev-kartik
This commit is contained in:
@@ -82,6 +82,9 @@ export class AiChatController {
|
|||||||
private async buildKnowledgeBase(auth: string): Promise<string> {
|
private async buildKnowledgeBase(auth: string): Promise<string> {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (this.knowledgeBase && now - this.kbLoadedAt < this.kbTtlMs) {
|
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;
|
return this.knowledgeBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +153,7 @@ export class AiChatController {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.warn(`Failed to fetch clinics: ${err}`);
|
this.logger.warn(`Failed to fetch clinics: ${err}`);
|
||||||
|
sections.push('## Clinics\nFailed to load clinic data.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -188,6 +192,7 @@ export class AiChatController {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.warn(`Failed to fetch health packages: ${err}`);
|
this.logger.warn(`Failed to fetch health packages: ${err}`);
|
||||||
|
sections.push('\n## Health Packages\nFailed to load package data.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -211,6 +216,7 @@ export class AiChatController {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.warn(`Failed to fetch insurance partners: ${err}`);
|
this.logger.warn(`Failed to fetch insurance partners: ${err}`);
|
||||||
|
sections.push('\n## Insurance Partners\nFailed to load insurance data.');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.knowledgeBase =
|
this.knowledgeBase =
|
||||||
@@ -241,7 +247,11 @@ ${kb}`;
|
|||||||
|
|
||||||
private async chatWithTools(userMessage: string, auth: string) {
|
private async chatWithTools(userMessage: string, auth: string) {
|
||||||
const kb = await this.buildKnowledgeBase(auth);
|
const kb = await this.buildKnowledgeBase(auth);
|
||||||
|
this.logger.log(`KB content preview: ${kb.substring(0, 300)}...`);
|
||||||
const systemPrompt = this.buildSystemPrompt(kb);
|
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 platformService = this.platform;
|
||||||
|
|
||||||
const { text, steps } = await generateText({
|
const { text, steps } = await generateText({
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export class CallAssistService {
|
|||||||
|
|
||||||
const apptResult = await this.platform.queryWithAuth<any>(
|
const apptResult = await this.platform.queryWithAuth<any>(
|
||||||
`{ appointments(first: 10, orderBy: [{ scheduledAt: DescNullsLast }]) { edges { node {
|
`{ appointments(first: 10, orderBy: [{ scheduledAt: DescNullsLast }]) { edges { node {
|
||||||
id scheduledAt appointmentStatus doctorName department reasonForVisit patientId
|
id scheduledAt status doctorName department reasonForVisit patientId
|
||||||
} } } }`,
|
} } } }`,
|
||||||
undefined,
|
undefined,
|
||||||
authHeader,
|
authHeader,
|
||||||
@@ -77,7 +77,7 @@ export class CallAssistService {
|
|||||||
? new Date(a.scheduledAt).toLocaleDateString('en-IN')
|
? new Date(a.scheduledAt).toLocaleDateString('en-IN')
|
||||||
: '?';
|
: '?';
|
||||||
parts.push(
|
parts.push(
|
||||||
`- ${date}: ${a.doctorName ?? '?'} (${a.department ?? '?'}) — ${a.appointmentStatus}`,
|
`- ${date}: ${a.doctorName ?? '?'} (${a.department ?? '?'}) — ${a.status}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export class SearchController {
|
|||||||
this.platform
|
this.platform
|
||||||
.queryWithAuth<any>(
|
.queryWithAuth<any>(
|
||||||
`{ appointments(first: 50, orderBy: [{ scheduledAt: DescNullsLast }]) { edges { node {
|
`{ appointments(first: 50, orderBy: [{ scheduledAt: DescNullsLast }]) { edges { node {
|
||||||
id scheduledAt doctorName department appointmentStatus patientId
|
id scheduledAt doctorName department status patientId
|
||||||
} } } }`,
|
} } } }`,
|
||||||
undefined,
|
undefined,
|
||||||
authHeader,
|
authHeader,
|
||||||
|
|||||||
@@ -169,6 +169,11 @@ export class MissedCallWebhookController {
|
|||||||
durationSec: data.duration,
|
durationSec: data.duration,
|
||||||
disposition: this.mapDisposition(data.disposition),
|
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) {
|
if (data.recordingUrl) {
|
||||||
callData.recording = {
|
callData.recording = {
|
||||||
primaryLinkUrl: data.recordingUrl,
|
primaryLinkUrl: data.recordingUrl,
|
||||||
|
|||||||
@@ -50,15 +50,16 @@ export class MissedQueueService implements OnModuleInit {
|
|||||||
let created = 0;
|
let created = 0;
|
||||||
let updated = 0;
|
let updated = 0;
|
||||||
|
|
||||||
|
// Ozonetel fromTime/toTime use HH:MM:SS format (time of day, filters within current day)
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const fiveMinAgo = new Date(now.getTime() - 5 * 60 * 1000);
|
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[];
|
let abandonCalls: any[];
|
||||||
try {
|
try {
|
||||||
abandonCalls = await this.ozonetel.getAbandonCalls({
|
abandonCalls = await this.ozonetel.getAbandonCalls({
|
||||||
fromTime: format(fiveMinAgo),
|
fromTime: toHHMMSS(fiveMinAgo),
|
||||||
toTime: format(now),
|
toTime: toHHMMSS(now),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.warn(`Failed to fetch abandon calls: ${err}`);
|
this.logger.warn(`Failed to fetch abandon calls: ${err}`);
|
||||||
|
|||||||
@@ -89,9 +89,9 @@ export class WorklistService {
|
|||||||
authHeader: string,
|
authHeader: string,
|
||||||
): Promise<any[]> {
|
): Promise<any[]> {
|
||||||
try {
|
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<any>(
|
const data = await this.platform.queryWithAuth<any>(
|
||||||
`{ 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
|
id name createdAt
|
||||||
direction callStatus agentName
|
direction callStatus agentName
|
||||||
callerNumber { primaryPhoneNumber }
|
callerNumber { primaryPhoneNumber }
|
||||||
|
|||||||
Reference in New Issue
Block a user