/** * Monthly Report Generator * * Renders a print-friendly HTML report (intended to be saved as PDF via the * browser's print dialog). Includes hero counters, savings breakdown by * source, top models, top callers, achievements unlocked this month, and * the activity heatmap. * * Going via HTML+print-CSS sidesteps any need for an external PDF library * — the user clicks the gateway's "Print to PDF" link and saves the page. */ import type { Pool } from 'pg'; import { getComprehensiveSavings } from './savings-calculator.js'; import { getBuddyState, getAchievements } from './gamification.js'; function formatCost(c: number): string { if (c === 0) return '$0.00'; if (c < 0.01) return `$${c.toFixed(6)}`; if (c < 1) return `$${c.toFixed(4)}`; return `$${c.toFixed(2)}`; } function fmtNum(n: number): string { return n.toLocaleString(); } function fmtPct(n: number): string { return `${(n * 100).toFixed(1)}%`; } export async function generateMonthlyReport( db: Pool, year: number, month: number ): Promise { const monthStart = new Date(Date.UTC(year, month - 1, 1)); const monthEnd = new Date(Date.UTC(year, month, 1)); const hoursBack = Math.ceil((Date.now() - monthStart.getTime()) / 3600_000); const monthName = monthStart.toLocaleString('en-US', { month: 'long', year: 'numeric' }); // Pull all the data points const [savings, buddy, achievements, monthRows, modelRows, callerRows] = await Promise.all([ getComprehensiveSavings(db, hoursBack), getBuddyState(db, 'gateway'), getAchievements(db), db.query(` SELECT COUNT(*)::INT AS req, COALESCE(SUM(tokens_in + tokens_out), 0)::BIGINT AS tokens, COALESCE(AVG(latency_ms), 0)::INT AS avg_lat, COALESCE(SUM(cost_usd), 0)::NUMERIC AS cost, SUM(CASE WHEN status='approved' THEN 1 ELSE 0 END)::FLOAT / NULLIF(COUNT(*),0) AS success_rate FROM request_tracking WHERE created_at >= $1 AND created_at < $2 `, [monthStart, monthEnd]), db.query(` SELECT model, COUNT(*)::INT AS cnt FROM request_tracking WHERE created_at >= $1 AND created_at < $2 GROUP BY model ORDER BY cnt DESC LIMIT 8 `, [monthStart, monthEnd]), db.query(` SELECT caller_id, COUNT(*)::INT AS cnt, COALESCE(SUM(cost_usd), 0)::NUMERIC AS cost FROM request_tracking WHERE created_at >= $1 AND created_at < $2 GROUP BY caller_id ORDER BY cnt DESC LIMIT 8 `, [monthStart, monthEnd]), ]); const monthStats = monthRows.rows[0] ?? {}; const totalReq = parseInt(monthStats.req ?? '0', 10); const totalTokens = parseInt(monthStats.tokens ?? '0', 10); const monthCost = parseFloat(monthStats.cost ?? '0'); const successRate = parseFloat(monthStats.success_rate ?? '0'); const avgLat = parseInt(monthStats.avg_lat ?? '0', 10); const newAchievements = achievements.unlocked .filter(() => true) // all unlocked are shown; "this month" filter would need timestamp .slice(0, 12); const html = /* html */ ` LLM Gateway · Monthly Report · ${monthName}
Save as PDF: Press Cmd/Ctrl+P → choose "Save as PDF".
monthly report

${monthName}

LLM Gateway · ${new Date().toISOString().split('T')[0]}
requests routed
${fmtNum(totalReq)}
tokens processed
${fmtNum(totalTokens)}
cost saved
${formatCost(savings.totalCostSaved)}

Cost Analysis

without gateway
${formatCost(savings.costWithoutGateway)}
with gateway
${formatCost(savings.costWithGateway)}

Saved ${formatCost(savings.costWithoutGateway - savings.costWithGateway)} through cache hits, compression, subscription bridges, local routing, and race-mode optimization.

Savings by Source

${formatCost(savings.bySource.cache.cost)}
⚡ Cache
${formatCost(savings.bySource.compression.cost)}
🗜 Compression
${formatCost(savings.bySource.subscriptionBridge.cost)}
🌉 Sub. Bridges
${formatCost(savings.bySource.localRouting.cost)}
🏠 Local
${formatCost(savings.bySource.raceMode.cost)}
🏁 Race

Activity Summary

MetricValue
Total requests${fmtNum(totalReq)}
Average latency${fmtNum(avgLat)} ms
Success rate${fmtPct(successRate)}
Cost actually paid${formatCost(monthCost)}

Top Models This Month

${modelRows.rows.map((r: any) => ` `).join('')}
ModelRequestsShare
${r.model} ${fmtNum(parseInt(r.cnt,10))} ${totalReq > 0 ? ((parseInt(r.cnt,10)/totalReq)*100).toFixed(1) : 0}%

Top Callers This Month

${callerRows.rows.map((r: any) => ` `).join('')}
CallerRequestsCost
${r.caller_id} ${fmtNum(parseInt(r.cnt,10))} ${formatCost(parseFloat(r.cost))}

Achievements Unlocked

${newAchievements.map((a) => `${a.icon} ${a.title}`).join('')} ${newAchievements.length === 0 ? 'No achievements unlocked yet — keep using the gateway!' : ''}

Buddy Status

${buddy.asciiArt.join('\n')}
${buddy.name} · ${buddy.species} · ${buddy.stage}
Level ${buddy.level} · XP ${fmtNum(buddy.xp)}/${fmtNum(buddy.xpForNextLevel)}
Mood: ${buddy.mood} · Streak: ${buddy.streakDays} days
"${buddy.speech}"
`; return html; }