// Cost accumulation and tracking system interface CostSession { id: string timestamp: number model: string provider: string tokenUsage: { promptTokens: number completionTokens: number totalTokens: number } cost: { inputCost: number outputCost: number totalCost: number currency: string } } interface CostSummary { totalSessions: number totalTokens: number totalCost: number currency: string startDate: number lastUpdated: number byProvider: Record byModel: Record } class CostTracker { private readonly STORAGE_KEY = 'pdf_translation_cost_tracker' // Get stored cost summary getCostSummary(): CostSummary { if (typeof window === 'undefined') { // Server-side fallback return this.getDefaultSummary() } try { const stored = localStorage.getItem(this.STORAGE_KEY) if (!stored) { return this.getDefaultSummary() } return JSON.parse(stored) } catch (error) { console.error('Error reading cost summary:', error) return this.getDefaultSummary() } } // Add new cost session addCostSession(session: Omit): CostSummary { if (typeof window === 'undefined') { // Server-side fallback return this.getDefaultSummary() } const summary = this.getCostSummary() const now = Date.now() const newSession: CostSession = { ...session, id: `session_${now}_${Math.random().toString(36).substr(2, 9)}`, timestamp: now } // Update summary summary.totalSessions += 1 summary.totalTokens += session.tokenUsage.totalTokens summary.totalCost += session.cost.totalCost summary.lastUpdated = now if (summary.startDate === 0) { summary.startDate = now } // Update by provider if (!summary.byProvider[session.provider]) { summary.byProvider[session.provider] = { sessions: 0, tokens: 0, cost: 0 } } summary.byProvider[session.provider].sessions += 1 summary.byProvider[session.provider].tokens += session.tokenUsage.totalTokens summary.byProvider[session.provider].cost += session.cost.totalCost // Update by model if (!summary.byModel[session.model]) { summary.byModel[session.model] = { sessions: 0, tokens: 0, cost: 0 } } summary.byModel[session.model].sessions += 1 summary.byModel[session.model].tokens += session.tokenUsage.totalTokens summary.byModel[session.model].cost += session.cost.totalCost // Save to localStorage try { localStorage.setItem(this.STORAGE_KEY, JSON.stringify(summary)) } catch (error) { console.error('Error saving cost summary:', error) } return summary } // Reset cost tracking resetCostTracking(): CostSummary { if (typeof window === 'undefined') { return this.getDefaultSummary() } const summary = this.getDefaultSummary() try { localStorage.setItem(this.STORAGE_KEY, JSON.stringify(summary)) } catch (error) { console.error('Error resetting cost tracking:', error) } return summary } // Export cost data exportCostData(): string { const summary = this.getCostSummary() return JSON.stringify(summary, null, 2) } // Format cost for display formatCost(amount: number, currency: string = 'USD'): string { return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency, minimumFractionDigits: 4, maximumFractionDigits: 4 }).format(amount) } // Format large numbers formatNumber(num: number): string { if (num >= 1000000) { return (num / 1000000).toFixed(1) + 'M' } else if (num >= 1000) { return (num / 1000).toFixed(1) + 'K' } return num.toString() } // Get cost insights getCostInsights(): { averageCostPerSession: number averageTokensPerSession: number mostUsedProvider: string mostUsedModel: string dailyAverage: number } { const summary = this.getCostSummary() if (summary.totalSessions === 0) { return { averageCostPerSession: 0, averageTokensPerSession: 0, mostUsedProvider: '', mostUsedModel: '', dailyAverage: 0 } } // Calculate averages const averageCostPerSession = summary.totalCost / summary.totalSessions const averageTokensPerSession = summary.totalTokens / summary.totalSessions // Find most used provider let mostUsedProvider = '' let maxProviderSessions = 0 Object.entries(summary.byProvider).forEach(([provider, stats]) => { if (stats.sessions > maxProviderSessions) { maxProviderSessions = stats.sessions mostUsedProvider = provider } }) // Find most used model let mostUsedModel = '' let maxModelSessions = 0 Object.entries(summary.byModel).forEach(([model, stats]) => { if (stats.sessions > maxModelSessions) { maxModelSessions = stats.sessions mostUsedModel = model } }) // Calculate daily average const daysSinceStart = summary.startDate > 0 ? Math.max(1, Math.ceil((Date.now() - summary.startDate) / (24 * 60 * 60 * 1000))) : 1 const dailyAverage = summary.totalCost / daysSinceStart return { averageCostPerSession, averageTokensPerSession, mostUsedProvider, mostUsedModel, dailyAverage } } private getDefaultSummary(): CostSummary { return { totalSessions: 0, totalTokens: 0, totalCost: 0, currency: 'USD', startDate: 0, lastUpdated: 0, byProvider: {}, byModel: {} } } } // Export singleton instance export const costTracker = new CostTracker() export type { CostSession, CostSummary }