mirror of
https://github.com/complexcaresolutions/whatsapp-bot.git
synced 2026-03-17 15:04:07 +00:00
Phase 1 implementation with all core modules: - Fastify webhook server with Meta signature validation - WhatsApp Cloud API client (send text/template/interactive, mark as read) - LLM abstraction layer with Claude provider (Haiku for speed) - BullMQ message processing pipeline (dedup, rate limiting) - Bot routing (MessageRouter, ConversationManager, EscalationManager) - Payload CMS integration (InteractionWriter via direct DB, RulesLoader, TemplateResolver) - Healthcare-safe system prompt with medical keyword detection - PM2 ecosystem config Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
89 lines
2.3 KiB
TypeScript
89 lines
2.3 KiB
TypeScript
import { getLogger } from '../lib/logger.js'
|
|
import type { PayloadClient } from './PayloadClient.js'
|
|
import { getConfig } from '../config.js'
|
|
import { DEFAULT_HEALTHCARE_PROMPT } from '../llm/HealthcarePrompt.js'
|
|
|
|
const log = getLogger('template-resolver')
|
|
|
|
interface CommunityTemplate {
|
|
id: number
|
|
name: string
|
|
category: string
|
|
content: string
|
|
tenant?: number | { id: number }
|
|
}
|
|
|
|
const TEMPLATE_CACHE_TTL = 600_000 // 10 minutes
|
|
|
|
export class TemplateResolver {
|
|
private systemPromptCache: string | null = null
|
|
private lastFetch = 0
|
|
|
|
constructor(private payloadClient: PayloadClient) {}
|
|
|
|
async getSystemPrompt(): Promise<string> {
|
|
const now = Date.now()
|
|
if (now - this.lastFetch < TEMPLATE_CACHE_TTL && this.systemPromptCache) {
|
|
return this.systemPromptCache
|
|
}
|
|
|
|
try {
|
|
const tenantId = getConfig().CCS_TENANT_ID
|
|
const result = await this.payloadClient.find<CommunityTemplate>(
|
|
'community-templates',
|
|
{
|
|
'where[category][equals]': 'whatsapp_system_prompt',
|
|
'where[tenant][equals]': tenantId,
|
|
limit: '1',
|
|
},
|
|
)
|
|
|
|
if (result.docs.length > 0) {
|
|
this.systemPromptCache = result.docs[0].content
|
|
this.lastFetch = now
|
|
log.info('System prompt loaded from CMS')
|
|
return this.systemPromptCache
|
|
}
|
|
} catch (err) {
|
|
log.error(
|
|
{ error: (err as Error).message },
|
|
'Failed to load system prompt from CMS',
|
|
)
|
|
}
|
|
|
|
// Fallback to hardcoded prompt
|
|
log.info('Using default healthcare prompt')
|
|
return DEFAULT_HEALTHCARE_PROMPT
|
|
}
|
|
|
|
async resolveTemplate(
|
|
category: string,
|
|
name?: string,
|
|
): Promise<string | null> {
|
|
try {
|
|
const tenantId = getConfig().CCS_TENANT_ID
|
|
const query: Record<string, unknown> = {
|
|
'where[category][equals]': category,
|
|
'where[tenant][equals]': tenantId,
|
|
limit: '1',
|
|
}
|
|
if (name) {
|
|
query['where[name][equals]'] = name
|
|
}
|
|
|
|
const result =
|
|
await this.payloadClient.find<CommunityTemplate>(
|
|
'community-templates',
|
|
query,
|
|
)
|
|
|
|
return result.docs[0]?.content ?? null
|
|
} catch (err) {
|
|
log.error(
|
|
{ category, name, error: (err as Error).message },
|
|
'Failed to resolve template',
|
|
)
|
|
return null
|
|
}
|
|
}
|
|
}
|