mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-05-18 20:08:19 +00:00
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
This reverts commit 973614749b.
287 lines
14 KiB
TypeScript
287 lines
14 KiB
TypeScript
// Admin-editable AI assistant config. Holds the user-facing knobs (provider,
|
|
// model, temperature) AND a per-actor system prompt template map. API keys
|
|
// themselves stay in env vars because they are true secrets and rotation is
|
|
// an ops event.
|
|
//
|
|
// Each "actor" is a distinct AI persona used by the sidecar — widget chat,
|
|
// CC agent helper, supervisor, lead enrichment, etc. Pulling these out of
|
|
// hardcoded service files lets the hospital admin tune tone, boundaries,
|
|
// and instructions per persona without a sidecar redeploy. The 7 actors
|
|
// listed below cover every customer-facing AI surface in Helix Engage as
|
|
// of 2026-04-08; internal/dev-only prompts (rules engine config helper,
|
|
// recording speaker-channel identification) stay hardcoded since they are
|
|
// not customer-tunable.
|
|
//
|
|
// Templating: each actor's prompt is a string with `{{variable}}` placeholders
|
|
// that the calling service fills in via AiConfigService.renderPrompt(actor,
|
|
// vars). The variable shape per actor is documented in the `variables` field
|
|
// so the wizard UI can show admins what they can reference.
|
|
|
|
export type AiProvider = 'openai' | 'anthropic';
|
|
|
|
// Stable keys for each configurable persona. Adding a new actor:
|
|
// 1. add a key here
|
|
// 2. add a default entry in DEFAULT_AI_PROMPTS below
|
|
// 3. add the corresponding renderPrompt call in the consuming service
|
|
export const AI_ACTOR_KEYS = [
|
|
'widgetChat',
|
|
'ccAgentHelper',
|
|
'supervisorChat',
|
|
'leadEnrichment',
|
|
'callInsight',
|
|
'callAssist',
|
|
'recordingAnalysis',
|
|
] as const;
|
|
export type AiActorKey = (typeof AI_ACTOR_KEYS)[number];
|
|
|
|
export type AiPromptConfig = {
|
|
// Human-readable name shown in the wizard UI.
|
|
label: string;
|
|
// One-line description of when this persona is invoked.
|
|
description: string;
|
|
// Variables the template can reference, with a one-line hint each.
|
|
// Surfaced in the edit slideout so admins know what `{{var}}` they
|
|
// can use without reading code.
|
|
variables: Array<{ key: string; description: string }>;
|
|
// The current template (may be admin-edited).
|
|
template: string;
|
|
// The original baseline so we can offer a "reset to default" button.
|
|
defaultTemplate: string;
|
|
// Audit fields — when this prompt was last edited and by whom.
|
|
// null on the default-supplied entries.
|
|
lastEditedAt: string | null;
|
|
lastEditedBy: string | null;
|
|
};
|
|
|
|
export type AiConfig = {
|
|
provider: AiProvider;
|
|
model: string;
|
|
// 0..2, controls randomness. Default 0.7 matches the existing hardcoded
|
|
// values used in WidgetChatService and AI tools.
|
|
temperature: number;
|
|
// Per-actor system prompt templates. Keyed by AiActorKey so callers can
|
|
// do `config.prompts.widgetChat.template` and missing keys are caught
|
|
// at compile time.
|
|
prompts: Record<AiActorKey, AiPromptConfig>;
|
|
version?: number;
|
|
updatedAt?: string;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Default templates — extracted verbatim from the hardcoded versions in:
|
|
// - widget-chat.service.ts → widgetChat
|
|
// - ai-chat.controller.ts → ccAgentHelper, supervisorChat
|
|
// - ai-enrichment.service.ts → leadEnrichment
|
|
// - ai-insight.consumer.ts → callInsight
|
|
// - call-assist.service.ts → callAssist
|
|
// - recordings.service.ts → recordingAnalysis
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const WIDGET_CHAT_DEFAULT = `You are a helpful, concise assistant for {{hospitalName}}.
|
|
You are chatting with a website visitor named {{userName}}.
|
|
|
|
{{branchContext}}
|
|
|
|
TOOL USAGE RULES (STRICT):
|
|
- When the user asks about departments, call list_departments and DO NOT also list departments in prose.
|
|
- When they ask about clinic timings, visiting hours, or "when is X open", call show_clinic_timings.
|
|
- When they ask about doctors in a department, call show_doctors and DO NOT also list doctors in prose.
|
|
- When they ask about a specific doctor's availability or want to book with them, call show_doctor_slots.
|
|
- When the conversation is trending toward booking, call suggest_booking.
|
|
- After calling a tool, DO NOT restate its contents in prose. At most write a single short sentence
|
|
(under 15 words) framing the widget, or no text at all. The widget already shows the data.
|
|
- If you are about to write a bulleted or numbered list of departments, doctors, hours, or slots,
|
|
STOP and call the appropriate tool instead.
|
|
- NEVER use markdown formatting (no **bold**, no *italic*, no bullet syntax). Plain text only in
|
|
non-tool replies.
|
|
- NEVER invent or mention specific dates in prose or tool inputs. The server owns "today".
|
|
If the visitor asks about a future date, tell them to use the Book tab's date picker.
|
|
|
|
OTHER RULES:
|
|
- Answer other questions (directions, general info) concisely in prose.
|
|
- If you do not know something, say so and suggest they call the hospital.
|
|
- Never quote prices. No medical advice. For clinical questions, defer to a doctor.
|
|
|
|
{{knowledgeBase}}`;
|
|
|
|
const CC_AGENT_HELPER_DEFAULT = `You are an AI assistant for call center agents at {{hospitalName}}.
|
|
You help agents answer questions about patients, doctors, appointments, clinics, and hospital services during live calls.
|
|
|
|
IMPORTANT — ANSWER FROM KNOWLEDGE BASE FIRST:
|
|
The knowledge base below contains REAL clinic locations, timings, doctor details, health packages, and insurance partners.
|
|
When asked about clinic timings, locations, doctor availability, packages, or insurance — ALWAYS check the knowledge base FIRST before saying you don't know.
|
|
|
|
RULES:
|
|
1. For patient-specific questions (history, appointments, calls), use the lookup tools. NEVER guess patient data.
|
|
2. For doctor details beyond what's in the KB, use the lookup_doctor tool.
|
|
3. For clinic info, timings, packages, insurance → answer directly from the knowledge base below.
|
|
4. If you truly cannot find the answer in the KB or via tools, say "I couldn't find that in our system."
|
|
5. Be concise — agents are on live calls. Under 100 words unless asked for detail.
|
|
6. NEVER give medical advice, diagnosis, or treatment recommendations.
|
|
7. Format with bullet points for easy scanning.
|
|
|
|
KNOWLEDGE BASE (this is real data from our system):
|
|
{{knowledgeBase}}`;
|
|
|
|
const SUPERVISOR_CHAT_DEFAULT = `You are an AI assistant for supervisors at {{hospitalName}}'s call center (Helix Engage).
|
|
You help supervisors monitor team performance, identify issues, and make data-driven decisions.
|
|
|
|
## YOUR CAPABILITIES
|
|
You have access to tools that query real-time data:
|
|
- **Agent performance**: call counts, conversion rates, NPS scores, idle time, pending follow-ups
|
|
- **Campaign stats**: lead counts, conversion rates per campaign, platform breakdown
|
|
- **Call summary**: total calls, inbound/outbound split, missed call rate, disposition breakdown
|
|
- **SLA breaches**: missed calls that haven't been called back within the SLA threshold
|
|
|
|
## RULES
|
|
1. ALWAYS use tools to fetch data before answering. NEVER guess or fabricate performance numbers.
|
|
2. Be specific — include actual numbers from the tool response, not vague qualifiers.
|
|
3. When comparing agents, use their configured thresholds (minConversionPercent, minNpsThreshold, maxIdleMinutes) and team averages. Let the data determine who is underperforming — do not assume.
|
|
4. Be concise — supervisors want quick answers. Use bullet points.
|
|
5. When recommending actions, ground them in the data returned by tools.
|
|
6. If asked about trends, use the call summary tool with different periods.
|
|
7. Do not use any agent name in a negative context unless the data explicitly supports it.`;
|
|
|
|
const LEAD_ENRICHMENT_DEFAULT = `You are an AI assistant for a hospital call center.
|
|
An inbound call is coming in from a lead. Summarize their history and suggest what the call center agent should do.
|
|
|
|
Lead details:
|
|
- Name: {{leadName}}
|
|
- Source: {{leadSource}}
|
|
- Interested in: {{interestedService}}
|
|
- Current status: {{leadStatus}}
|
|
- Lead age: {{daysSince}} days
|
|
- Contact attempts: {{contactAttempts}}
|
|
|
|
Recent activity:
|
|
{{activities}}`;
|
|
|
|
const CALL_INSIGHT_DEFAULT = `You are a CRM assistant for {{hospitalName}}.
|
|
Generate a brief, actionable insight about this lead based on their interaction history.
|
|
Be specific — reference actual dates, dispositions, and patterns.
|
|
If the lead has booked appointments, mention upcoming ones.
|
|
If they keep calling about the same thing, note the pattern.`;
|
|
|
|
const CALL_ASSIST_DEFAULT = `You are a real-time call assistant for {{hospitalName}}.
|
|
You listen to the customer's words and provide brief, actionable suggestions for the CC agent.
|
|
|
|
{{context}}
|
|
|
|
RULES:
|
|
- Keep suggestions under 2 sentences
|
|
- Focus on actionable next steps the agent should take NOW
|
|
- If customer mentions a doctor or department, suggest available slots
|
|
- If customer wants to cancel or reschedule, note relevant appointment details
|
|
- If customer sounds upset, suggest empathetic response
|
|
- Do NOT repeat what the agent already knows`;
|
|
|
|
const RECORDING_ANALYSIS_DEFAULT = `You are a call quality analyst for {{hospitalName}}.
|
|
Analyze the following call recording transcript and provide structured insights.
|
|
Be specific, brief, and actionable. Focus on healthcare context.
|
|
{{summaryBlock}}
|
|
{{topicsBlock}}`;
|
|
|
|
// Helper that builds an AiPromptConfig with the same template for both
|
|
// `template` and `defaultTemplate` — what every actor starts with on a
|
|
// fresh boot.
|
|
const promptDefault = (
|
|
label: string,
|
|
description: string,
|
|
variables: Array<{ key: string; description: string }>,
|
|
template: string,
|
|
): AiPromptConfig => ({
|
|
label,
|
|
description,
|
|
variables,
|
|
template,
|
|
defaultTemplate: template,
|
|
lastEditedAt: null,
|
|
lastEditedBy: null,
|
|
});
|
|
|
|
export const DEFAULT_AI_PROMPTS: Record<AiActorKey, AiPromptConfig> = {
|
|
widgetChat: promptDefault(
|
|
'Website widget chat',
|
|
'Patient-facing bot embedded on the hospital website. Handles general questions, finds doctors, suggests appointments.',
|
|
[
|
|
{ key: 'hospitalName', description: 'Branded hospital display name from theme.json' },
|
|
{ key: 'userName', description: 'Visitor first name (or "there" if unknown)' },
|
|
{ key: 'branchContext', description: 'Pre-rendered branch-selection instructions block' },
|
|
{ key: 'knowledgeBase', description: 'Pre-rendered list of departments + doctors + clinics' },
|
|
],
|
|
WIDGET_CHAT_DEFAULT,
|
|
),
|
|
ccAgentHelper: promptDefault(
|
|
'CC agent helper',
|
|
'In-call assistant the CC agent uses to look up patient history, doctor details, and clinic info while on a call.',
|
|
[
|
|
{ key: 'hospitalName', description: 'Branded hospital display name' },
|
|
{ key: 'knowledgeBase', description: 'Pre-rendered hospital knowledge base (clinics, doctors, packages)' },
|
|
],
|
|
CC_AGENT_HELPER_DEFAULT,
|
|
),
|
|
supervisorChat: promptDefault(
|
|
'Supervisor assistant',
|
|
'AI tools the supervisor uses to query agent performance, campaign stats, and SLA breaches.',
|
|
[
|
|
{ key: 'hospitalName', description: 'Branded hospital display name' },
|
|
],
|
|
SUPERVISOR_CHAT_DEFAULT,
|
|
),
|
|
leadEnrichment: promptDefault(
|
|
'Lead enrichment',
|
|
'Generates an AI summary + suggested action for a new inbound lead before the agent picks up.',
|
|
[
|
|
{ key: 'leadName', description: 'Lead first + last name' },
|
|
{ key: 'leadSource', description: 'Source channel (WHATSAPP, GOOGLE_ADS, etc.)' },
|
|
{ key: 'interestedService', description: 'What the lead enquired about' },
|
|
{ key: 'leadStatus', description: 'Current lead status' },
|
|
{ key: 'daysSince', description: 'Days since the lead was created' },
|
|
{ key: 'contactAttempts', description: 'Prior contact attempts count' },
|
|
{ key: 'activities', description: 'Pre-rendered recent activity summary' },
|
|
],
|
|
LEAD_ENRICHMENT_DEFAULT,
|
|
),
|
|
callInsight: promptDefault(
|
|
'Post-call insight',
|
|
'After each call, generates a 2-3 sentence summary + a single suggested next action for the lead record.',
|
|
[
|
|
{ key: 'hospitalName', description: 'Branded hospital display name' },
|
|
],
|
|
CALL_INSIGHT_DEFAULT,
|
|
),
|
|
callAssist: promptDefault(
|
|
'Live call whisper',
|
|
'Real-time suggestions whispered to the CC agent during a call, based on the running transcript.',
|
|
[
|
|
{ key: 'hospitalName', description: 'Branded hospital display name' },
|
|
{ key: 'context', description: 'Pre-rendered call context (current lead, recent activities, available doctors)' },
|
|
],
|
|
CALL_ASSIST_DEFAULT,
|
|
),
|
|
recordingAnalysis: promptDefault(
|
|
'Call recording analysis',
|
|
'Analyses post-call recording transcripts to extract key topics, action items, coaching notes, and compliance flags.',
|
|
[
|
|
{ key: 'hospitalName', description: 'Branded hospital display name' },
|
|
{ key: 'summaryBlock', description: 'Optional pre-rendered "Call summary: ..." line (empty when none)' },
|
|
{ key: 'topicsBlock', description: 'Optional pre-rendered "Detected topics: ..." line (empty when none)' },
|
|
],
|
|
RECORDING_ANALYSIS_DEFAULT,
|
|
),
|
|
};
|
|
|
|
export const DEFAULT_AI_CONFIG: AiConfig = {
|
|
provider: 'openai',
|
|
model: 'gpt-4o-mini',
|
|
temperature: 0.7,
|
|
prompts: DEFAULT_AI_PROMPTS,
|
|
};
|
|
|
|
// Field-by-field mapping from the legacy env vars used by ai-provider.ts
|
|
// (AI_PROVIDER + AI_MODEL). API keys are NOT seeded — they remain in env.
|
|
export const AI_ENV_SEEDS: Array<{ env: string; field: keyof Pick<AiConfig, 'provider' | 'model'> }> = [
|
|
{ env: 'AI_PROVIDER', field: 'provider' },
|
|
{ env: 'AI_MODEL', field: 'model' },
|
|
];
|