import React, { useState, useEffect, useMemo } from 'react'; import { Card, CardContent, Typography, Box, Grid, Chip, Tooltip, Button, Alert, CircularProgress, Accordion, AccordionSummary, AccordionDetails, } from '@mui/material'; import { ExpandMore } from '@mui/icons-material'; import { motion } from 'framer-motion'; import { Lightbulb, TrendingDown, DollarSign, ArrowRight, Info, Sparkles } from 'lucide-react'; // Types import { UsageLog } from '../../types/billing'; // Services import { billingService, formatCurrency } from '../../services/billingService'; interface CostOptimizationRecommendationsProps { userId?: string; terminalTheme?: boolean; } interface OptimizationRecommendation { id: string; title: string; description: string; potentialSavings: number; savingsPercentage: number; category: 'model_switch' | 'provider_switch' | 'usage_pattern' | 'efficiency'; priority: 'high' | 'medium' | 'low'; actionItems: string[]; currentCost: number; recommendedCost: number; } const CostOptimizationRecommendations: React.FC = ({ userId, terminalTheme = false }) => { const [usageLogs, setUsageLogs] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchUsageLogs = async () => { try { setLoading(true); setError(null); const currentDate = new Date(); const billingPeriod = `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, '0')}`; const response = await billingService.getUsageLogs(1000, 0, undefined, undefined, billingPeriod); setUsageLogs(response.logs || []); } catch (err) { console.error('[CostOptimizationRecommendations] Error fetching usage logs:', err); setError(err instanceof Error ? err.message : 'Failed to fetch usage logs'); } finally { setLoading(false); } }; fetchUsageLogs(); }, [userId]); // Analyze usage patterns and generate recommendations const recommendations = useMemo(() => { if (usageLogs.length === 0) return []; const recs: OptimizationRecommendation[] = []; // 1. Model Switch Recommendations // Analyze if user is using expensive models when cheaper alternatives exist const modelUsage = usageLogs.reduce((acc, log) => { if (!log.model_used || log.cost_total === 0) return acc; const key = `${log.provider}:${log.model_used}`; if (!acc[key]) { acc[key] = { cost: 0, calls: 0, tokens: 0 }; } acc[key].cost += log.cost_total; acc[key].calls += 1; acc[key].tokens += log.tokens_total; return acc; }, {} as Record); // Check for Gemini Pro usage when Flash could work const geminiProUsage = Object.entries(modelUsage).find(([key]) => key.includes('gemini') && key.includes('pro') && !key.includes('flash') ); if (geminiProUsage) { const [_, data] = geminiProUsage; // Estimate Flash cost (typically 10-20x cheaper) const estimatedFlashCost = data.cost * 0.1; // Conservative estimate const savings = data.cost - estimatedFlashCost; if (savings > 0.01) { // Only show if savings > $0.01 recs.push({ id: 'gemini-pro-to-flash', title: 'Switch Gemini Pro to Flash for Simple Tasks', description: `You're using Gemini Pro for ${data.calls} calls. Consider using Gemini Flash for simpler tasks - it's 10x cheaper with similar quality for most use cases.`, potentialSavings: savings, savingsPercentage: (savings / data.cost) * 100, category: 'model_switch', priority: 'high', actionItems: [ 'Use Gemini Flash for content generation', 'Use Gemini Flash for simple Q&A', 'Reserve Gemini Pro for complex reasoning tasks only' ], currentCost: data.cost, recommendedCost: estimatedFlashCost }); } } // 2. Provider Cost Analysis const providerCosts = usageLogs.reduce((acc, log) => { if (log.cost_total === 0) return acc; if (!acc[log.provider]) { acc[log.provider] = { cost: 0, calls: 0, avgCostPerCall: 0 }; } acc[log.provider].cost += log.cost_total; acc[log.provider].calls += 1; acc[log.provider].avgCostPerCall = acc[log.provider].cost / acc[log.provider].calls; return acc; }, {} as Record); // Find expensive providers const sortedProviders = Object.entries(providerCosts) .sort(([, a], [, b]) => b.avgCostPerCall - a.avgCostPerCall); if (sortedProviders.length > 1) { const [mostExpensive, secondMost] = sortedProviders; const [expensiveProvider, expensiveData] = mostExpensive; const [alternativeProvider, alternativeData] = secondMost; // If expensive provider has significantly higher cost per call if (expensiveData.avgCostPerCall > alternativeData.avgCostPerCall * 1.5 && expensiveData.calls > 10) { const estimatedAlternativeCost = expensiveData.calls * alternativeData.avgCostPerCall; const savings = expensiveData.cost - estimatedAlternativeCost; if (savings > 0.01) { recs.push({ id: `provider-switch-${expensiveProvider}`, title: `Consider ${alternativeProvider} for Some Operations`, description: `${expensiveProvider} costs $${expensiveData.avgCostPerCall.toFixed(4)} per call on average, while ${alternativeProvider} costs $${alternativeData.avgCostPerCall.toFixed(4)}. Consider switching for non-critical operations.`, potentialSavings: savings, savingsPercentage: (savings / expensiveData.cost) * 100, category: 'provider_switch', priority: 'medium', actionItems: [ `Use ${alternativeProvider} for batch operations`, `Reserve ${expensiveProvider} for high-priority tasks only`, 'Review operation requirements to identify switchable tasks' ], currentCost: expensiveData.cost, recommendedCost: estimatedAlternativeCost }); } } } // 3. Usage Pattern Analysis - High Token Usage const highTokenUsage = usageLogs.filter(log => log.tokens_total > 10000 && log.cost_total > 0.01 ); if (highTokenUsage.length > 5) { const totalHighTokenCost = highTokenUsage.reduce((sum, log) => sum + log.cost_total, 0); const avgTokensPerCall = highTokenUsage.reduce((sum, log) => sum + log.tokens_total, 0) / highTokenUsage.length; recs.push({ id: 'optimize-high-token-usage', title: 'Optimize High Token Usage Operations', description: `You have ${highTokenUsage.length} operations using >10K tokens each. Consider breaking down large requests or using more efficient prompts.`, potentialSavings: totalHighTokenCost * 0.15, // Estimate 15% savings savingsPercentage: 15, category: 'usage_pattern', priority: 'medium', actionItems: [ 'Break down large requests into smaller chunks', 'Use more concise prompts', 'Implement result caching for repeated queries', `Average tokens per call: ${Math.round(avgTokensPerCall).toLocaleString()}` ], currentCost: totalHighTokenCost, recommendedCost: totalHighTokenCost * 0.85 }); } // 4. Efficiency - Failed Requests const failedRequests = usageLogs.filter(log => log.status === 'failed' && log.cost_total > 0); if (failedRequests.length > 0) { const failedCost = failedRequests.reduce((sum, log) => sum + log.cost_total, 0); const failureRate = (failedRequests.length / usageLogs.length) * 100; if (failureRate > 5) { // More than 5% failure rate recs.push({ id: 'reduce-failed-requests', title: 'Reduce Failed API Requests', description: `${failedRequests.length} requests failed (${failureRate.toFixed(1)}% failure rate), costing $${failedCost.toFixed(4)}. Improve error handling and retry logic.`, potentialSavings: failedCost * 0.8, // Can save 80% by preventing failures savingsPercentage: 80, category: 'efficiency', priority: 'high', actionItems: [ 'Review and fix error-prone operations', 'Implement better retry logic', 'Add input validation before API calls', 'Monitor error patterns and address root causes' ], currentCost: failedCost, recommendedCost: failedCost * 0.2 }); } } // Sort by priority and potential savings return recs.sort((a, b) => { const priorityOrder = { high: 3, medium: 2, low: 1 }; if (priorityOrder[a.priority] !== priorityOrder[b.priority]) { return priorityOrder[b.priority] - priorityOrder[a.priority]; } return b.potentialSavings - a.potentialSavings; }); }, [usageLogs]); const totalPotentialSavings = recommendations.reduce((sum, rec) => sum + rec.potentialSavings, 0); const totalCurrentCost = usageLogs.reduce((sum, log) => sum + log.cost_total, 0); if (loading) { return ( Analyzing usage patterns... ); } if (error) { return ( {error} ); } if (recommendations.length === 0) { return ( Great Job! Your usage patterns are already optimized. No recommendations at this time. ); } return ( Cost Optimization Recommendations {/* Summary */} {formatCurrency(totalPotentialSavings)} Potential Monthly Savings {recommendations.length} recommendation{recommendations.length !== 1 ? 's' : ''} • {totalCurrentCost > 0 ? ` ${((totalPotentialSavings / totalCurrentCost) * 100).toFixed(1)}% of current spending` : ''} {/* Recommendations List */} {recommendations.map((rec, index) => ( } sx={{ px: 2, py: 1.5 }} > {rec.title} {rec.description} {formatCurrency(rec.potentialSavings)} {rec.savingsPercentage.toFixed(1)}% savings Current Cost {formatCurrency(rec.currentCost)} Recommended Cost {formatCurrency(rec.recommendedCost)} Action Items: {rec.actionItems.map((item, idx) => (
  • {item}
  • ))}
    ))}
    ); }; export default CostOptimizationRecommendations;