import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { generateObject } from 'ai'; import type { LanguageModel } from 'ai'; import { z } from 'zod'; import { createAiModel } from './ai-provider'; type LeadContext = { firstName?: string; lastName?: string; leadSource?: string; interestedService?: string; leadStatus?: string; contactAttempts?: number; createdAt?: string; campaignId?: string; activities?: { activityType: string; summary: string }[]; }; type EnrichmentResult = { aiSummary: string; aiSuggestedAction: string; }; const enrichmentSchema = z.object({ aiSummary: z.string().describe('1-2 sentence summary of who this lead is and their history'), aiSuggestedAction: z.string().describe('5-10 word suggested action for the agent'), }); @Injectable() export class AiEnrichmentService { private readonly logger = new Logger(AiEnrichmentService.name); private readonly aiModel: LanguageModel | null; constructor(private config: ConfigService) { this.aiModel = createAiModel(config); if (!this.aiModel) { this.logger.warn('AI not configured — enrichment uses fallback'); } } async enrichLead(lead: LeadContext): Promise { if (!this.aiModel) { return this.fallbackEnrichment(lead); } try { const daysSince = lead.createdAt ? Math.floor((Date.now() - new Date(lead.createdAt).getTime()) / (1000 * 60 * 60 * 24)) : 0; const activitiesText = lead.activities?.length ? lead.activities.map(a => `- ${a.activityType}: ${a.summary}`).join('\n') : 'No previous interactions'; const { object } = await generateObject({ model: this.aiModel!, schema: enrichmentSchema, prompt: `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: ${lead.firstName ?? 'Unknown'} ${lead.lastName ?? ''} - Source: ${lead.leadSource ?? 'Unknown'} - Interested in: ${lead.interestedService ?? 'Unknown'} - Current status: ${lead.leadStatus ?? 'Unknown'} - Lead age: ${daysSince} days - Contact attempts: ${lead.contactAttempts ?? 0} Recent activity: ${activitiesText}`, }); this.logger.log(`AI enrichment generated for lead ${lead.firstName} ${lead.lastName}`); return object; } catch (error) { this.logger.error(`AI enrichment failed: ${error}`); return this.fallbackEnrichment(lead); } } private fallbackEnrichment(lead: LeadContext): EnrichmentResult { const daysSince = lead.createdAt ? Math.floor((Date.now() - new Date(lead.createdAt).getTime()) / (1000 * 60 * 60 * 24)) : 0; const attempts = lead.contactAttempts ?? 0; const service = lead.interestedService ?? 'general inquiry'; const source = lead.leadSource?.replace(/_/g, ' ').toLowerCase() ?? 'unknown source'; let summary: string; let action: string; if (attempts === 0) { summary = `First-time inquiry from ${source}. Interested in ${service}. Lead is ${daysSince} day(s) old with no previous contact.`; action = `Introduce services and offer appointment booking`; } else if (attempts === 1) { summary = `Returning inquiry — contacted once before. Interested in ${service} via ${source}. Lead is ${daysSince} day(s) old.`; action = `Follow up on previous conversation, offer appointment`; } else { summary = `Repeat contact (${attempts} previous attempts) for ${service}. Originally from ${source}, ${daysSince} day(s) old. Shows high interest.`; action = `Prioritize appointment booking — high-intent lead`; } return { aiSummary: summary, aiSuggestedAction: action }; } }