mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-05-18 20:08:19 +00:00
feat: suggestion rules engine + caller context evaluation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { PlatformGraphqlService } from '../platform/platform-graphql.service';
|
||||
import { SessionService } from '../auth/session.service';
|
||||
import { evaluateSuggestionRules, type SuggestionTrigger } from '../rules-engine/suggestion-rules';
|
||||
|
||||
export type CallerContext = {
|
||||
leadId: string;
|
||||
@@ -39,6 +40,8 @@ export type CallerContext = {
|
||||
occurredAt: string;
|
||||
outcome: string | null;
|
||||
}>;
|
||||
// Rule-driven suggestion triggers
|
||||
suggestionTriggers: SuggestionTrigger[];
|
||||
};
|
||||
|
||||
const CACHE_KEY_PREFIX = 'caller:context:';
|
||||
@@ -123,6 +126,26 @@ export class CallerContextService {
|
||||
const firstName = lead.contactName?.firstName ?? '';
|
||||
const lastName = lead.contactName?.lastName ?? '';
|
||||
|
||||
const appointments = (appointmentsData?.appointments?.edges ?? []).map((e: any) => e.node);
|
||||
const calls = (callsData?.calls?.edges ?? []).map((e: any) => ({
|
||||
startedAt: e.node.startedAt,
|
||||
direction: e.node.direction,
|
||||
duration: e.node.durationSec,
|
||||
disposition: e.node.disposition,
|
||||
agentName: e.node.agentName,
|
||||
}));
|
||||
|
||||
const suggestionTriggers = evaluateSuggestionRules({
|
||||
isNew: false,
|
||||
interestedService: lead.interestedService ?? null,
|
||||
leadStatus: lead.status ?? null,
|
||||
contactAttempts: lead.contactAttempts ?? 0,
|
||||
appointments,
|
||||
calls: calls.map((c: any) => ({ direction: c.direction, disposition: c.disposition, startedAt: c.startedAt })),
|
||||
utmCampaign: lead.utmCampaign ?? null,
|
||||
leadSource: lead.source ?? null,
|
||||
});
|
||||
|
||||
return {
|
||||
leadId,
|
||||
patientId: patientId || lead.patientId || '',
|
||||
@@ -136,15 +159,10 @@ export class CallerContextService {
|
||||
contactAttempts: lead.contactAttempts ?? 0,
|
||||
lastContacted: lead.lastContacted ?? null,
|
||||
utmCampaign: lead.utmCampaign ?? null,
|
||||
appointments: (appointmentsData?.appointments?.edges ?? []).map((e: any) => e.node),
|
||||
calls: (callsData?.calls?.edges ?? []).map((e: any) => ({
|
||||
startedAt: e.node.startedAt,
|
||||
direction: e.node.direction,
|
||||
duration: e.node.durationSec,
|
||||
disposition: e.node.disposition,
|
||||
agentName: e.node.agentName,
|
||||
})),
|
||||
appointments,
|
||||
calls,
|
||||
activities: (activitiesData?.leadActivities?.edges ?? []).map((e: any) => e.node),
|
||||
suggestionTriggers,
|
||||
};
|
||||
} catch (err: any) {
|
||||
this.logger.warn(`[CALLER-CTX] Build failed: ${err.message}`);
|
||||
@@ -152,6 +170,22 @@ export class CallerContextService {
|
||||
}
|
||||
}
|
||||
|
||||
renderSuggestionsForPrompt(triggers: SuggestionTrigger[]): string {
|
||||
if (triggers.length === 0) return '';
|
||||
const lines = [
|
||||
'',
|
||||
'SUGGESTION RULES (from business configuration):',
|
||||
'Based on this caller\'s profile, the following suggestions should be offered.',
|
||||
'Generate a natural, conversational script for each that the agent can read aloud.',
|
||||
'Return them in the `suggestions` array of your JSON response.',
|
||||
'',
|
||||
];
|
||||
triggers.forEach((t, i) => {
|
||||
lines.push(`${i + 1}. [${t.type}/${t.priority}] ${t.title} — ${t.reason}`);
|
||||
});
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
renderForPrompt(ctx: CallerContext): string {
|
||||
const lines: string[] = [];
|
||||
lines.push(`## CURRENT CALLER: ${ctx.name}`);
|
||||
|
||||
Reference in New Issue
Block a user