import React from 'react'; import { GlassCard } from './GlassCard'; import { formatCurrency } from '../utils/formatters'; import type { CostData } from '../utils/costLogger'; import './CostView.scss'; export interface CostViewProps { costHistory: CostData[]; className?: string; } interface ModelCostSummary { model: string; totalCost: number; usageCount: number; inputTokens: number; outputTokens: number; cachedTokens: number; } export const CostView: React.FC = ({ costHistory, className = '' }) => { // Calculate cost summaries by model const modelSummaries = React.useMemo(() => { const summaryMap = new Map(); costHistory.forEach(cost => { const existing = summaryMap.get(cost.model) || { model: cost.model, totalCost: 0, usageCount: 0, inputTokens: 0, outputTokens: 0, cachedTokens: 0, }; existing.totalCost += cost.cost; existing.usageCount += 1; existing.inputTokens += cost.inputTokens; existing.outputTokens += cost.outputTokens; existing.cachedTokens += cost.cachedTokens || 0; summaryMap.set(cost.model, existing); }); return Array.from(summaryMap.values()).sort((a, b) => b.totalCost - a.totalCost); }, [costHistory]); const totalCost = modelSummaries.reduce((sum, model) => sum + model.totalCost, 0); const totalUsage = modelSummaries.reduce((sum, model) => sum + model.usageCount, 0); const formatModelName = (model: string) => { // Clean up model names for display return model .replace(/^(openai\/|anthropic\/|google\/)/, '') .replace(/-/g, ' ') .split(' ') .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(' '); }; return (

Cost Breakdown

Total: {formatCurrency(totalCost)}
Total Requests {totalUsage}
Models Used {modelSummaries.length}
Avg Cost/Request {totalUsage > 0 ? formatCurrency(totalCost / totalUsage) : '$0.00'}
{modelSummaries.map((summary) => { const percentage = totalCost > 0 ? (summary.totalCost / totalCost) * 100 : 0; return (
{formatModelName(summary.model)} {formatCurrency(summary.totalCost)}
{summary.usageCount} request{summary.usageCount !== 1 ? 's' : ''} {summary.inputTokens.toLocaleString()} in / {summary.outputTokens.toLocaleString()} out {summary.cachedTokens > 0 && ( {summary.cachedTokens.toLocaleString()} cached )}
); })}
{modelSummaries.length === 0 && (

No cost data available yet

)} ); };