mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-05-18 20:08:19 +00:00
feat: config-driven flow runtime engine for WhatsApp conversations
Groups + Blocks execution model adapted from Typebot:
- FlowExecutionService: walks through groups/blocks, pauses at InputBlocks
- FlowSessionService: Redis-backed session state (24h TTL)
- FlowStoreService: loads flow definitions from data/flows/ JSON files
- FlowVariableService: {{variable}} interpolation + expressions
- ToolRegistry: registered tool handlers (departments, doctors, slots, booking)
- Default appointment-booking.json flow seeded on first run
MessagingService delegates to flow engine when published flows exist,
falls back to hardcoded AI chat otherwise (backward compatible).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
44
src/messaging/flow/flow-variable.service.ts
Normal file
44
src/messaging/flow/flow-variable.service.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class FlowVariableService {
|
||||
// Replace {{variableName}} with values from session variables
|
||||
interpolate(template: string, variables: Record<string, any>): string {
|
||||
return template.replace(/\{\{(\w+)\}\}/g, (match, name) => {
|
||||
const value = variables[name];
|
||||
if (value === undefined || value === null) return match; // keep placeholder if unresolved
|
||||
if (typeof value === 'object') return JSON.stringify(value);
|
||||
return String(value);
|
||||
});
|
||||
}
|
||||
|
||||
// Interpolate all string values in an object
|
||||
interpolateObject(obj: Record<string, string>, variables: Record<string, any>): Record<string, any> {
|
||||
const result: Record<string, any> = {};
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
result[key] = this.interpolate(value, variables);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Execute expressions for SetVariableBlock
|
||||
evaluateExpression(expression: string, value: string, variables: Record<string, any>): any {
|
||||
switch (expression) {
|
||||
case 'extract_id': {
|
||||
// Extract UUID from "doc:{uuid}:{name}" or "dept:{name}" or "slot:{id}:{datetime}"
|
||||
const parts = value.split(':');
|
||||
return parts.length >= 2 ? parts[1] : value;
|
||||
}
|
||||
case 'date_tomorrow': {
|
||||
const d = new Date(Date.now() + 86400000);
|
||||
return d.toISOString().split('T')[0];
|
||||
}
|
||||
case 'date_day_after': {
|
||||
const d = new Date(Date.now() + 2 * 86400000);
|
||||
return d.toISOString().split('T')[0];
|
||||
}
|
||||
default:
|
||||
return this.interpolate(value, variables);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user