import { libx } from 'libx.js/build/bundles/essentials.js'; /** * RequestLogger - Tracks and logs LLM API requests with metrics */ export class RequestLogger { private enabled: boolean; private logs: RequestLog[] = []; private maxLogs: number = 1000; constructor(enabled: boolean = false) { this.enabled = enabled; } /** * Start tracking a new request */ public startRequest(provider: string, model: string, metadata?: Record): RequestTracker { const tracker = new RequestTracker(provider, model, metadata); if (this.enabled) { libx.log.v('RequestLogger: started tracking request', { requestId: tracker.requestId, provider, model }); } return tracker; } /** * Log a completed request */ public logRequest(log: RequestLog): void { if (!this.enabled) return; this.logs.push(log); // Trim logs if exceeding max if (this.logs.length > this.maxLogs) { this.logs = this.logs.slice(-this.maxLogs); } libx.log.i('RequestLogger: request completed', { requestId: log.requestId, provider: log.provider, model: log.model, latency: log.latencyMs, success: log.success, tokensUsed: log.tokensUsed }); } /** * Get request statistics */ public getStats(provider?: string): RequestStats { const filteredLogs = provider ? this.logs.filter(log => log.provider === provider) : this.logs; if (filteredLogs.length === 0) { return { totalRequests: 0, successfulRequests: 0, failedRequests: 0, averageLatency: 0, totalTokensUsed: 0, providerBreakdown: {} }; } const totalRequests = filteredLogs.length; const successfulRequests = filteredLogs.filter(log => log.success).length; const failedRequests = totalRequests - successfulRequests; const totalLatency = filteredLogs.reduce((sum, log) => sum + log.latencyMs, 0); const averageLatency = totalLatency / totalRequests; const totalTokensUsed = filteredLogs.reduce((sum, log) => sum + (log.tokensUsed || 0), 0); // Provider breakdown const providerBreakdown: Record = {}; for (const log of filteredLogs) { if (!providerBreakdown[log.provider]) { providerBreakdown[log.provider] = { requests: 0, avgLatency: 0, tokens: 0 }; } providerBreakdown[log.provider].requests++; providerBreakdown[log.provider].tokens += log.tokensUsed || 0; } // Calculate average latency per provider for (const [prov, stats] of Object.entries(providerBreakdown)) { const provLogs = filteredLogs.filter(log => log.provider === prov); const provLatency = provLogs.reduce((sum, log) => sum + log.latencyMs, 0); stats.avgLatency = provLatency / provLogs.length; } return { totalRequests, successfulRequests, failedRequests, averageLatency, totalTokensUsed, providerBreakdown }; } /** * Clear all logs */ public clearLogs(): void { this.logs = []; libx.log.v('RequestLogger: cleared all logs'); } /** * Get recent logs */ public getRecentLogs(count: number = 10): RequestLog[] { return this.logs.slice(-count); } } /** * RequestTracker - Tracks a single request lifecycle */ export class RequestTracker { public requestId: string; public provider: string; public model: string; public metadata?: Record; private startTime: number; constructor(provider: string, model: string, metadata?: Record) { this.requestId = this.generateRequestId(); this.provider = provider; this.model = model; this.metadata = metadata; this.startTime = Date.now(); } /** * End tracking and return log entry */ public end(success: boolean, tokensUsed?: number, error?: string): RequestLog { const endTime = Date.now(); const latencyMs = endTime - this.startTime; return { requestId: this.requestId, provider: this.provider, model: this.model, timestamp: new Date(this.startTime).toISOString(), latencyMs, success, tokensUsed, error, metadata: this.metadata }; } private generateRequestId(): string { return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } } export interface RequestLog { requestId: string; provider: string; model: string; timestamp: string; latencyMs: number; success: boolean; tokensUsed?: number; error?: string; metadata?: Record; } export interface RequestStats { totalRequests: number; successfulRequests: number; failedRequests: number; averageLatency: number; totalTokensUsed: number; providerBreakdown: Record; } export interface ProviderStats { requests: number; avgLatency: number; tokens: number; } // Singleton instance let globalLogger: RequestLogger | null = null; export function getRequestLogger(enabled: boolean = true): RequestLogger { if (!globalLogger) { globalLogger = new RequestLogger(enabled); } return globalLogger; }