| |
| |
| |
| |
| |
| |
|
|
| import { ProcessedEmailRecord } from "@/types"; |
|
|
| export interface TelemetryMetric { |
| key: string; |
| label: string; |
| value: number; |
| unit: string; |
| category: "revenue" | "efficiency" | "intelligence" | "risk"; |
| trend: "up" | "down" | "flat"; |
| changePercent: number; |
| description: string; |
| } |
|
|
| export interface UserTelemetry { |
| user: string; |
| email: string; |
| totalEmails: number; |
| processedEmails: number; |
| metrics: TelemetryMetric[]; |
| revenuePerEmail: number; |
| totalEstimatedRevenue: number; |
| totalTimeSavedHours: number; |
| efficiencyScore: number; |
| topSenders: { sender: string; count: number; estimatedValue: number }[]; |
| categoryBreakdown: { category: string; count: number; revenue: number }[]; |
| revenueTimeline: { date: string; revenue: number; emails: number }[]; |
| insights: TelemetryInsight[]; |
| } |
|
|
| export interface TelemetryInsight { |
| id: string; |
| type: "opportunity" | "risk" | "efficiency" | "revenue"; |
| severity: "high" | "medium" | "low"; |
| title: string; |
| description: string; |
| estimatedValue: number; |
| actionable: boolean; |
| recommendedAction: string; |
| } |
|
|
| export interface TelemetryReport { |
| generatedAt: string; |
| user: string; |
| totalUsers: number; |
| aggregateMetrics: TelemetryMetric[]; |
| users: UserTelemetry[]; |
| topInsights: TelemetryInsight[]; |
| revenueByCategory: { category: string; revenue: number; count: number }[]; |
| efficiencyGains: { metric: string; before: number; after: number; improvement: number }[]; |
| } |
|
|
| |
|
|
| const VALUATION = { |
| |
| categoryRevenue: { |
| report: 450, |
| data: 620, |
| financial: 850, |
| research: 1200, |
| document: 280, |
| general: 150, |
| } as Record<string, number>, |
|
|
| |
| categoryTimeSaved: { |
| report: 2.5, |
| data: 3.2, |
| financial: 4.0, |
| research: 5.5, |
| document: 1.5, |
| general: 0.8, |
| } as Record<string, number>, |
|
|
| |
| analystRate: 200, |
|
|
| |
| confidenceMultiplier: 0.85, |
|
|
| |
| baseEfficiency: 65, |
| }; |
|
|
| |
|
|
| function computeTrend(before: number, after: number): { trend: "up" | "down" | "flat"; changePercent: number } { |
| if (before === 0) { |
| return { trend: after > 0 ? "up" : "flat", changePercent: 0 }; |
| } |
| const changePercent = ((after - before) / before) * 100; |
| const trend: "up" | "down" | "flat" = changePercent > 0 ? "up" : changePercent < 0 ? "down" : "flat"; |
| return { trend, changePercent: Math.round(changePercent * 10) / 10 }; |
| } |
|
|
| function computeImprovement(before: number, after: number): number { |
| if (before === 0) return 0; |
| return Math.round(((before - after) / before) * 1000) / 10; |
| } |
|
|
| export function generateTelemetry( |
| records: ProcessedEmailRecord[], |
| userEmail: string = "dr.gilead@mailbox.local" |
| ): TelemetryReport { |
| try { |
| const generatedAt = new Date().toISOString(); |
|
|
| |
| const validRecords = records.filter((r): r is ProcessedEmailRecord => |
| r != null && typeof r === "object" && "id" in r |
| ); |
|
|
| const userRecords = groupByUser(validRecords, userEmail); |
| const users = userRecords.map((group) => |
| buildUserTelemetry(group.records, group.user, group.email) |
| ); |
|
|
| const aggregateMetrics = buildAggregateMetrics(users); |
| const topInsights = users.flatMap((u) => u.insights).sort( |
| (a, b) => b.estimatedValue - a.estimatedValue |
| ).slice(0, 10); |
|
|
| const revenueByCategory = buildRevenueByCategory(records); |
| const efficiencyGains = buildEfficiencyGains(records, users); |
|
|
| return { |
| generatedAt, |
| user: userEmail, |
| totalUsers: users.length, |
| aggregateMetrics, |
| users, |
| topInsights, |
| revenueByCategory, |
| efficiencyGains, |
| }; |
| } catch (e) { |
| console.error("[telemetry/engine] generateTelemetry error:", e); |
| return { |
| generatedAt: new Date().toISOString(), |
| user: userEmail, |
| totalUsers: 0, |
| aggregateMetrics: [], |
| users: [], |
| topInsights: [], |
| revenueByCategory: [], |
| efficiencyGains: [], |
| }; |
| } |
| } |
|
|
| function buildUserTelemetry( |
| records: ProcessedEmailRecord[], |
| user: string, |
| email: string |
| ): UserTelemetry { |
| const totalEmails = records.length; |
| const processedEmails = records.filter((r) => r.processedAt).length; |
|
|
| |
| const categoryBreakdown = buildCategoryBreakdown(records); |
| const totalEstimatedRevenue = records.reduce((sum, r) => { |
| const baseRevenue = VALUATION.categoryRevenue[r.category] || VALUATION.categoryRevenue.general; |
| const fieldBonus = (r.fieldCount || 0) * 2.5; |
| const tableBonus = (r.tableCount || 0) * 5; |
| const confidenceAdj = (r.confidence || 0.85) * VALUATION.confidenceMultiplier; |
| return sum + (baseRevenue + fieldBonus + tableBonus) * confidenceAdj; |
| }, 0); |
|
|
| const revenuePerEmail = totalEmails > 0 ? totalEstimatedRevenue / totalEmails : 0; |
|
|
| |
| const totalTimeSavedHours = records.reduce((sum, r) => { |
| const hours = VALUATION.categoryTimeSaved[r.category] || VALUATION.categoryTimeSaved.general; |
| return sum + hours * (r.confidence || 0.85); |
| }, 0); |
|
|
| |
| const extractionRate = totalEmails > 0 |
| ? records.reduce((sum, r) => sum + (r.fieldCount + r.tableCount * 3), 0) / totalEmails |
| : 0; |
| const automationRate = totalEmails > 0 ? processedEmails / totalEmails : 0; |
| const efficiencyScore = Math.min( |
| 100, |
| VALUATION.baseEfficiency + |
| extractionRate * 2 + |
| automationRate * 20 + |
| Math.min(totalTimeSavedHours / totalEmails, 10) * 1.5 |
| ); |
|
|
| |
| const senderMap = new Map<string, { count: number; estimatedValue: number }>(); |
| for (const r of records) { |
| const existing = senderMap.get(r.sender) || { count: 0, estimatedValue: 0 }; |
| const value = (VALUATION.categoryRevenue[r.category] || 150) * (r.confidence || 0.85); |
| existing.count++; |
| existing.estimatedValue += value; |
| senderMap.set(r.sender, existing); |
| } |
| const topSenders = Array.from(senderMap.entries()) |
| .map(([sender, data]) => ({ sender, ...data })) |
| .sort((a, b) => b.estimatedValue - a.estimatedValue) |
| .slice(0, 10); |
|
|
| |
| const timelineMap = new Map<string, { revenue: number; emails: number }>(); |
| for (const r of records) { |
| const date = r.receivedDate?.split("T")[0] || r.processedAt?.split("T")[0] || "unknown"; |
| const existing = timelineMap.get(date) || { revenue: 0, emails: 0 }; |
| const value = (VALUATION.categoryRevenue[r.category] || 150) * (r.confidence || 0.85); |
| existing.revenue += value; |
| existing.emails++; |
| timelineMap.set(date, existing); |
| } |
| const revenueTimeline = Array.from(timelineMap.entries()) |
| .map(([date, data]) => ({ date, ...data })) |
| .sort((a, b) => a.date.localeCompare(b.date)); |
|
|
| |
| const metrics: TelemetryMetric[] = [ |
| { |
| key: "total_revenue", |
| label: "Total Estimated Revenue", |
| value: Math.round(totalEstimatedRevenue), |
| unit: "USD", |
| category: "revenue", |
| ...computeTrend(0, 0), |
| description: `Revenue potential from ${totalEmails} processed emails`, |
| }, |
| { |
| key: "revenue_per_email", |
| label: "Revenue per Email", |
| value: Math.round(revenuePerEmail), |
| unit: "USD", |
| category: "revenue", |
| ...computeTrend(0, 0), |
| description: "Average revenue generated per processed email", |
| }, |
| { |
| key: "time_saved", |
| label: "Total Time Saved", |
| value: Math.round(totalTimeSavedHours), |
| unit: "hours", |
| category: "efficiency", |
| ...computeTrend(0, 0), |
| description: `Equivalent to $${Math.round(totalTimeSavedHours * VALUATION.analystRate).toLocaleString()} in analyst labor`, |
| }, |
| { |
| key: "efficiency_score", |
| label: "Efficiency Score", |
| value: Math.round(efficiencyScore), |
| unit: "/100", |
| category: "efficiency", |
| ...computeTrend(0, 0), |
| description: "Composite score from extraction rate, automation, and time saved", |
| }, |
| { |
| key: "data_points", |
| label: "Data Points Extracted", |
| value: records.reduce((s, r) => s + (r.fieldCount || 0) + (r.tableCount || 0) * 10, 0), |
| unit: "fields", |
| category: "intelligence", |
| ...computeTrend(0, 0), |
| description: "Structured fields and table rows extracted from attachments", |
| }, |
| { |
| key: "automation_rate", |
| label: "Automation Rate", |
| value: Math.round(automationRate * 100), |
| unit: "%", |
| category: "efficiency", |
| ...computeTrend(0, 0), |
| description: "Percentage of emails processed without manual intervention", |
| }, |
| ]; |
|
|
| |
| const insights = buildInsights(records, totalEstimatedRevenue, totalTimeSavedHours, efficiencyScore); |
|
|
| return { |
| user, |
| email, |
| totalEmails, |
| processedEmails, |
| metrics, |
| revenuePerEmail: Math.round(revenuePerEmail), |
| totalEstimatedRevenue: Math.round(totalEstimatedRevenue), |
| totalTimeSavedHours: Math.round(totalTimeSavedHours), |
| efficiencyScore: Math.round(efficiencyScore), |
| topSenders, |
| categoryBreakdown, |
| revenueTimeline, |
| insights, |
| }; |
| } |
|
|
| function buildCategoryBreakdown(records: ProcessedEmailRecord[]) { |
| const catMap = new Map<string, { count: number; revenue: number }>(); |
| for (const r of records) { |
| const existing = catMap.get(r.category) || { count: 0, revenue: 0 }; |
| existing.count++; |
| existing.revenue += (VALUATION.categoryRevenue[r.category] || 150) * (r.confidence || 0.85); |
| catMap.set(r.category, existing); |
| } |
| return Array.from(catMap.entries()) |
| .map(([category, data]) => ({ category, ...data, revenue: Math.round(data.revenue) })) |
| .sort((a, b) => b.revenue - a.revenue); |
| } |
|
|
| function buildAggregateMetrics(users: UserTelemetry[]): TelemetryMetric[] { |
| const totalRevenue = users.reduce((s, u) => s + u.totalEstimatedRevenue, 0); |
| const totalTimeSaved = users.reduce((s, u) => s + u.totalTimeSavedHours, 0); |
| const totalEmails = users.reduce((s, u) => s + u.totalEmails, 0); |
| const avgEfficiency = users.length > 0 |
| ? users.reduce((s, u) => s + u.efficiencyScore, 0) / users.length |
| : 0; |
|
|
| return [ |
| { |
| key: "agg_revenue", |
| label: "Aggregate Revenue Potential", |
| value: Math.round(totalRevenue), |
| unit: "USD", |
| category: "revenue", |
| ...computeTrend(0, 0), |
| description: `Total revenue across ${users.length} user(s) and ${totalEmails} emails`, |
| }, |
| { |
| key: "agg_time_saved", |
| label: "Aggregate Time Saved", |
| value: Math.round(totalTimeSaved), |
| unit: "hours", |
| category: "efficiency", |
| ...computeTrend(0, 0), |
| description: `Equivalent to $${Math.round(totalTimeSaved * VALUATION.analystRate).toLocaleString()} in labor savings`, |
| }, |
| { |
| key: "agg_efficiency", |
| label: "Average Efficiency Score", |
| value: Math.round(avgEfficiency), |
| unit: "/100", |
| category: "efficiency", |
| ...computeTrend(0, 0), |
| description: "Mean efficiency score across all users", |
| }, |
| { |
| key: "agg_emails", |
| label: "Total Emails Processed", |
| value: totalEmails, |
| unit: "emails", |
| category: "intelligence", |
| ...computeTrend(0, 0), |
| description: "Total emails ingested into the telemetry pipeline", |
| }, |
| ]; |
| } |
|
|
| function buildRevenueByCategory(records: ProcessedEmailRecord[]) { |
| const catMap = new Map<string, { revenue: number; count: number }>(); |
| for (const r of records) { |
| const category = r.category ?? "unknown"; |
| const existing = catMap.get(category) || { revenue: 0, count: 0 }; |
| existing.revenue += (VALUATION.categoryRevenue[category] || 150) * (r.confidence || 0.85); |
| existing.count++; |
| catMap.set(category, existing); |
| } |
| return Array.from(catMap.entries()) |
| .map(([category, data]) => ({ |
| category, |
| revenue: Math.round(data.revenue), |
| count: data.count, |
| })) |
| .sort((a, b) => b.revenue - a.revenue); |
| } |
|
|
| function buildEfficiencyGains(records: ProcessedEmailRecord[], users: UserTelemetry[]) { |
| const totalEmails = records.length; |
| const avgFieldsPerEmail = totalEmails > 0 |
| ? records.reduce((s, r) => s + (r.fieldCount || 0), 0) / totalEmails |
| : 0; |
| const avgTimeSaved = users.length > 0 |
| ? users.reduce((s, u) => s + u.totalTimeSavedHours, 0) / users.length |
| : 0; |
|
|
| return [ |
| { |
| metric: "Manual Data Entry β Automated Extraction", |
| before: 45, |
| after: 2, |
| improvement: computeImprovement(45, 2), |
| }, |
| { |
| metric: "Email Triage β AI Categorization", |
| before: 15, |
| after: 0.5, |
| improvement: computeImprovement(15, 0.5), |
| }, |
| { |
| metric: "Attachment Parsing β Structured Fields", |
| before: 30, |
| after: 1, |
| improvement: computeImprovement(30, 1), |
| }, |
| { |
| metric: "Revenue Identification β Telemetry Scoring", |
| before: 60, |
| after: Math.max(0.1, avgTimeSaved / Math.max(totalEmails, 1)), |
| improvement: computeImprovement(60, Math.max(0.1, avgTimeSaved / Math.max(totalEmails, 1))), |
| }, |
| ]; |
| } |
|
|
| function buildInsights( |
| records: ProcessedEmailRecord[], |
| totalRevenue: number, |
| totalTimeSaved: number, |
| efficiencyScore: number |
| ): TelemetryInsight[] { |
| const insights: TelemetryInsight[] = []; |
| let id = 0; |
|
|
| |
| const senderValue = new Map<string, number>(); |
| for (const r of records) { |
| const v = (VALUATION.categoryRevenue[r.category] || 150) * (r.confidence || 0.85); |
| senderValue.set(r.sender, (senderValue.get(r.sender) || 0) + v); |
| } |
| const topSender = Array.from(senderValue.entries()).sort((a, b) => b[1] - a[1])[0]; |
| if (topSender && topSender[1] > 500) { |
| insights.push({ |
| id: `insight-${id++}`, |
| type: "revenue", |
| severity: "high", |
| title: `High-value sender: ${topSender[0]}`, |
| description: `${topSender[0]} has generated $${Math.round(topSender[1])} in estimated revenue potential across ${records.filter(r => r.sender === topSender[0]).length} emails.`, |
| estimatedValue: Math.round(topSender[1]), |
| actionable: true, |
| recommendedAction: `Prioritize responses to ${topSender[0]} and set up automated alerts for new emails from this sender.`, |
| }); |
| } |
|
|
| |
| const researchEmails = records.filter(r => r.category === "research"); |
| if (researchEmails.length > 0) { |
| const researchValue = researchEmails.reduce((s, r) => |
| s + VALUATION.categoryRevenue.research * (r.confidence || 0.85), 0); |
| insights.push({ |
| id: `insight-${id++}`, |
| type: "opportunity", |
| severity: "high", |
| title: "Research emails show high capital potential", |
| description: `${researchEmails.length} research-related emails with $${Math.round(researchValue)} estimated value. Research emails have the highest per-email revenue ($${VALUATION.categoryRevenue.research}/email).`, |
| estimatedValue: Math.round(researchValue), |
| actionable: true, |
| recommendedAction: "Extract and catalog research findings. Cross-reference with grant/funding opportunities to maximize capital conversion.", |
| }); |
| } |
|
|
| |
| if (efficiencyScore < 80) { |
| const gap = 80 - efficiencyScore; |
| insights.push({ |
| id: `insight-${id++}`, |
| type: "efficiency", |
| severity: "medium", |
| title: "Efficiency improvement opportunity", |
| description: `Current efficiency score is ${Math.round(efficiencyScore)}/100. Closing the gap to 80 could save an additional ${Math.round(gap * 0.5)} hours/week.`, |
| estimatedValue: Math.round(gap * 0.5 * VALUATION.analystRate), |
| actionable: true, |
| recommendedAction: "Enable auto-processing for routine email categories and increase LLM extraction confidence thresholds.", |
| }); |
| } |
|
|
| |
| const totalFields = records.reduce((s, r) => s + (r.fieldCount || 0), 0); |
| if (totalFields > 100) { |
| insights.push({ |
| id: `insight-${id++}`, |
| type: "opportunity", |
| severity: "medium", |
| title: "High data extraction volume", |
| description: `${totalFields} structured fields extracted from ${records.length} emails. This data corpus can be monetized through analytics products or licensing.`, |
| estimatedValue: totalFields * 15, |
| actionable: true, |
| recommendedAction: "Aggregate extracted fields into a searchable knowledge base. Consider packaging as a data product for stakeholders.", |
| }); |
| } |
|
|
| |
| const financialEmails = records.filter(r => r.category === "financial"); |
| if (financialEmails.length > 0) { |
| const finValue = financialEmails.reduce((s, r) => |
| s + VALUATION.categoryRevenue.financial * (r.confidence || 0.85), 0); |
| insights.push({ |
| id: `insight-${id++}`, |
| type: "revenue", |
| severity: "high", |
| title: "Financial emails require immediate attention", |
| description: `${financialEmails.length} financial/billing emails with $${Math.round(finValue)} estimated value. Delayed processing may impact cash flow.`, |
| estimatedValue: Math.round(finValue), |
| actionable: true, |
| recommendedAction: "Set up real-time alerts for financial emails and auto-route to accounting workflow.", |
| }); |
| } |
|
|
| |
| if (totalTimeSaved > 20) { |
| insights.push({ |
| id: `insight-${id++}`, |
| type: "efficiency", |
| severity: "low", |
| title: "Significant time savings achieved", |
| description: `${Math.round(totalTimeSaved)} hours saved through automated processing β equivalent to $${Math.round(totalTimeSaved * VALUATION.analystRate).toLocaleString()} in analyst labor.`, |
| estimatedValue: Math.round(totalTimeSaved * VALUATION.analystRate), |
| actionable: false, |
| recommendedAction: "Reinvest saved time into higher-value analysis tasks and revenue generation activities.", |
| }); |
| } |
|
|
| return insights; |
| } |
|
|
| |
|
|
| function groupByUser( |
| records: ProcessedEmailRecord[], |
| defaultEmail: string |
| ): { user: string; email: string; records: ProcessedEmailRecord[] }[] { |
| |
| |
| |
| return [{ |
| user: "Dr. Gilead", |
| email: defaultEmail, |
| records, |
| }]; |
| } |
|
|
| |
| |
| |
| |
| export function generateMCPContext(report: TelemetryReport) { |
| return { |
| resources: [ |
| { |
| uri: "telemetry://summary", |
| name: "Telemetry Summary", |
| mimeType: "application/json", |
| text: JSON.stringify({ |
| totalRevenue: report.aggregateMetrics[0]?.value || 0, |
| totalTimeSaved: report.aggregateMetrics[1]?.value || 0, |
| efficiencyScore: report.aggregateMetrics[2]?.value || 0, |
| totalEmails: report.aggregateMetrics[3]?.value || 0, |
| }), |
| }, |
| { |
| uri: "telemetry://insights", |
| name: "Top Revenue Insights", |
| mimeType: "application/json", |
| text: JSON.stringify(report.topInsights), |
| }, |
| { |
| uri: "telemetry://categories", |
| name: "Revenue by Category", |
| mimeType: "application/json", |
| text: JSON.stringify(report.revenueByCategory), |
| }, |
| ], |
| tools: [ |
| { |
| name: "get_revenue_report", |
| description: "Get the full revenue telemetry report for the mailbox", |
| inputSchema: { type: "object", properties: {} }, |
| }, |
| { |
| name: "get_user_metrics", |
| description: "Get per-user efficiency and revenue metrics", |
| inputSchema: { type: "object", properties: {} }, |
| }, |
| { |
| name: "get_insights", |
| description: "Get actionable insights sorted by estimated value", |
| inputSchema: { type: "object", properties: {} }, |
| }, |
| { |
| name: "get_efficiency_gains", |
| description: "Get before/after efficiency comparison metrics", |
| inputSchema: { type: "object", properties: {} }, |
| }, |
| ], |
| }; |
| } |
|
|