← back to Daily Reporting
report-generator.ts
336 lines
/**
* Daily Report Generator for DW-Agents
*
* Generates comprehensive morning and end-of-day reports
*/
import fetch from 'node-fetch';
import { AGENT_HIERARCHY, getAgentsByDepartment, getAgentsByLevel, type Agent } from './agent-hierarchy';
export interface AgentMetrics {
agentId: string;
agentName: string;
status: 'online' | 'offline' | 'degraded';
uptime: number;
responseTime: number;
tasksCompleted: number;
errorsToday: number;
customMetrics: Record<string, any>;
lastActivity: string;
qnaReadiness: number; // 0-100 score
}
export interface DepartmentReport {
department: string;
agentsCount: number;
onlineCount: number;
totalTasks: number;
issues: string[];
highlights: string[];
metrics: AgentMetrics[];
}
export interface DailyReport {
reportType: 'morning' | 'eod';
timestamp: string;
executiveSummary: {
systemHealth: number;
agentsOnline: number;
totalAgents: number;
criticalIssues: number;
tasksCompletedToday: number;
topAchievements: string[];
};
departmentReports: DepartmentReport[];
issuesRequiringAttention: Array<{
severity: 'critical' | 'high' | 'medium' | 'low';
agent: string;
issue: string;
actionRequired: string;
}>;
actionItems: string[];
qnaStatus: {
overallReadiness: number;
agentReadiness: Array<{ agent: string; score: number; recentActivities: string[] }>;
};
}
/**
* Fetch metrics from an agent
*/
async function fetchAgentMetrics(agent: Agent): Promise<AgentMetrics> {
try {
const response = await fetch(`${agent.url}/api/metrics`, {
timeout: 5000,
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json() as any;
return {
agentId: agent.id,
agentName: agent.name,
status: 'online',
uptime: data.uptime || 0,
responseTime: data.responseTime || 0,
tasksCompleted: data.tasksCompleted || 0,
errorsToday: data.errorsToday || 0,
customMetrics: data.customMetrics || {},
lastActivity: data.lastActivity || new Date().toISOString(),
qnaReadiness: data.qnaReadiness || 75
};
} catch (error) {
console.error(`Failed to fetch metrics from ${agent.name}:`, error);
// Return offline status with default values
return {
agentId: agent.id,
agentName: agent.name,
status: 'offline',
uptime: 0,
responseTime: 0,
tasksCompleted: 0,
errorsToday: 0,
customMetrics: {},
lastActivity: 'N/A',
qnaReadiness: 0
};
}
}
/**
* Fetch metrics from all agents concurrently
*/
async function fetchAllAgentMetrics(): Promise<Map<string, AgentMetrics>> {
const metricsMap = new Map<string, AgentMetrics>();
const promises = AGENT_HIERARCHY.map(agent =>
fetchAgentMetrics(agent).then(metrics => {
metricsMap.set(agent.id, metrics);
})
);
await Promise.allSettled(promises);
return metricsMap;
}
/**
* Generate department reports
*/
function generateDepartmentReports(metricsMap: Map<string, AgentMetrics>): DepartmentReport[] {
const departments = getAgentsByDepartment();
const reports: DepartmentReport[] = [];
for (const [department, agents] of departments) {
const metrics = agents
.map(agent => metricsMap.get(agent.id))
.filter(m => m !== undefined) as AgentMetrics[];
const onlineCount = metrics.filter(m => m.status === 'online').length;
const totalTasks = metrics.reduce((sum, m) => sum + m.tasksCompleted, 0);
const issues: string[] = [];
const highlights: string[] = [];
metrics.forEach(m => {
if (m.status === 'offline') {
issues.push(`${m.agentName} is offline`);
} else if (m.errorsToday > 10) {
issues.push(`${m.agentName} has ${m.errorsToday} errors today`);
}
if (m.tasksCompleted > 50) {
highlights.push(`${m.agentName} completed ${m.tasksCompleted} tasks`);
}
});
reports.push({
department,
agentsCount: agents.length,
onlineCount,
totalTasks,
issues,
highlights,
metrics
});
}
return reports;
}
/**
* Generate executive summary
*/
function generateExecutiveSummary(
metricsMap: Map<string, AgentMetrics>,
departmentReports: DepartmentReport[]
): DailyReport['executiveSummary'] {
const allMetrics = Array.from(metricsMap.values());
const onlineCount = allMetrics.filter(m => m.status === 'online').length;
const totalTasks = allMetrics.reduce((sum, m) => sum + m.tasksCompleted, 0);
const criticalIssues = departmentReports.reduce((sum, dept) => {
return sum + dept.issues.filter(i => i.includes('offline') || i.includes('critical')).length;
}, 0);
const topAchievements: string[] = [];
departmentReports.forEach(dept => {
if (dept.highlights.length > 0) {
topAchievements.push(...dept.highlights.slice(0, 2));
}
});
const systemHealth = Math.round((onlineCount / allMetrics.length) * 100);
return {
systemHealth,
agentsOnline: onlineCount,
totalAgents: allMetrics.length,
criticalIssues,
tasksCompletedToday: totalTasks,
topAchievements: topAchievements.slice(0, 5)
};
}
/**
* Identify issues requiring attention
*/
function identifyIssues(
metricsMap: Map<string, AgentMetrics>,
departmentReports: DepartmentReport[]
): DailyReport['issuesRequiringAttention'] {
const issues: DailyReport['issuesRequiringAttention'] = [];
for (const [agentId, metrics] of metricsMap) {
if (metrics.status === 'offline') {
issues.push({
severity: 'critical',
agent: metrics.agentName,
issue: 'Agent is offline',
actionRequired: 'Restart agent and investigate cause'
});
}
if (metrics.errorsToday > 20) {
issues.push({
severity: 'high',
agent: metrics.agentName,
issue: `${metrics.errorsToday} errors detected today`,
actionRequired: 'Review error logs and fix underlying issues'
});
}
if (metrics.responseTime > 2000) {
issues.push({
severity: 'medium',
agent: metrics.agentName,
issue: `Slow response time (${metrics.responseTime}ms)`,
actionRequired: 'Optimize performance or scale resources'
});
}
if (metrics.qnaReadiness < 50) {
issues.push({
severity: 'medium',
agent: metrics.agentName,
issue: 'Low Q&A readiness score',
actionRequired: 'Update agent knowledge base and test responses'
});
}
}
return issues.sort((a, b) => {
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
return severityOrder[a.severity] - severityOrder[b.severity];
});
}
/**
* Generate Q&A readiness status
*/
function generateQnaStatus(metricsMap: Map<string, AgentMetrics>): DailyReport['qnaStatus'] {
const allMetrics = Array.from(metricsMap.values());
const avgReadiness = allMetrics.reduce((sum, m) => sum + m.qnaReadiness, 0) / allMetrics.length;
const agentReadiness = allMetrics
.map(m => ({
agent: m.agentName,
score: m.qnaReadiness,
recentActivities: [
`${m.tasksCompleted} tasks completed`,
`Last active: ${m.lastActivity}`,
`Status: ${m.status}`
]
}))
.sort((a, b) => b.score - a.score);
return {
overallReadiness: Math.round(avgReadiness),
agentReadiness
};
}
/**
* Generate action items
*/
function generateActionItems(
departmentReports: DepartmentReport[],
issues: DailyReport['issuesRequiringAttention']
): string[] {
const actionItems: string[] = [];
// Critical actions from issues
issues.slice(0, 5).forEach(issue => {
actionItems.push(`[${issue.severity.toUpperCase()}] ${issue.agent}: ${issue.actionRequired}`);
});
// Department-specific actions
departmentReports.forEach(dept => {
if (dept.onlineCount < dept.agentsCount) {
actionItems.push(`${dept.department}: Bring ${dept.agentsCount - dept.onlineCount} offline agents back online`);
}
});
return actionItems;
}
/**
* Generate complete daily report
*/
export async function generateDailyReport(reportType: 'morning' | 'eod'): Promise<DailyReport> {
console.log(`Generating ${reportType} report...`);
// Fetch all metrics
const metricsMap = await fetchAllAgentMetrics();
// Generate department reports
const departmentReports = generateDepartmentReports(metricsMap);
// Generate executive summary
const executiveSummary = generateExecutiveSummary(metricsMap, departmentReports);
// Identify issues
const issuesRequiringAttention = identifyIssues(metricsMap, departmentReports);
// Generate Q&A status
const qnaStatus = generateQnaStatus(metricsMap);
// Generate action items
const actionItems = generateActionItems(departmentReports, issuesRequiringAttention);
return {
reportType,
timestamp: new Date().toISOString(),
executiveSummary,
departmentReports,
issuesRequiringAttention,
actionItems,
qnaStatus
};
}