Files
ALwrity/frontend/src/components/billing/ComprehensiveAPIBreakdown.tsx

415 lines
16 KiB
TypeScript

import React from 'react';
import {
Card,
CardContent,
Typography,
Box,
Grid,
Chip,
Tooltip,
Accordion,
AccordionSummary,
AccordionDetails,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
} from '@mui/material';
import { ExpandMore } from '@mui/icons-material';
import { motion } from 'framer-motion';
import {
Info,
DollarSign,
Activity,
Zap,
Search,
Image,
Code,
Database,
Globe,
FileText,
BarChart3
} from 'lucide-react';
// Types
import { ProviderBreakdown } from '../../types/billing';
interface ComprehensiveAPIBreakdownProps {
providerBreakdown: ProviderBreakdown;
totalCost: number;
}
// Comprehensive API categories and their descriptions
const API_CATEGORIES = {
llm_models: {
title: 'Large Language Models',
description: 'AI models for text generation, analysis, and processing',
icon: <Code size={20} />,
apis: [
{
name: 'Gemini',
description: 'Google\'s advanced AI model for complex reasoning and coding',
models: ['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-1.5-pro', 'gemini-1.5-flash'],
pricing: 'From $0.10/1M tokens (Flash-Lite) to $15.00/1M tokens (Pro)',
use_cases: ['Content generation', 'Code analysis', 'Complex reasoning']
},
{
name: 'OpenAI',
description: 'GPT models for natural language processing and generation',
models: ['gpt-4o', 'gpt-4o-mini', 'gpt-3.5-turbo'],
pricing: 'From $0.15/1M tokens (GPT-4o Mini) to $10.00/1M tokens (GPT-4o)',
use_cases: ['Chat completion', 'Text analysis', 'Creative writing']
},
{
name: 'Anthropic',
description: 'Claude models for safe and helpful AI assistance',
models: ['claude-3.5-sonnet', 'claude-3-haiku', 'claude-3-opus'],
pricing: 'From $3.00/1M tokens (Sonnet) to $15.00/1M tokens (Opus)',
use_cases: ['Safe AI assistance', 'Long-form content', 'Analysis tasks']
},
{
name: 'Mistral',
description: 'European AI models for efficient text processing',
models: ['mistral-large', 'mistral-medium', 'mistral-small'],
pricing: 'From $2.00/1M tokens (Small) to $8.00/1M tokens (Large)',
use_cases: ['Multilingual support', 'Efficient processing', 'European compliance']
}
]
},
search_apis: {
title: 'Search & Research APIs',
description: 'APIs for web search, content discovery, and research',
icon: <Search size={20} />,
apis: [
{
name: 'Tavily',
description: 'AI-powered search for real-time information',
models: ['tavily-search'],
pricing: '$0.001 per search request',
use_cases: ['Real-time search', 'Fact checking', 'Research assistance']
},
{
name: 'Serper',
description: 'Google Search API for web results',
models: ['serper-search'],
pricing: '$0.001 per search request',
use_cases: ['Web search', 'SEO analysis', 'Content research']
},
{
name: 'Metaphor',
description: 'Advanced search and content discovery',
models: ['metaphor-search'],
pricing: '$0.003 per search request',
use_cases: ['Content discovery', 'Link analysis', 'Research automation']
}
]
},
content_processing: {
title: 'Content Processing APIs',
description: 'APIs for web scraping, content extraction, and processing',
icon: <FileText size={20} />,
apis: [
{
name: 'Firecrawl',
description: 'Web scraping and content extraction service',
models: ['firecrawl-extract', 'firecrawl-scrape'],
pricing: '$0.002 per page crawled',
use_cases: ['Web scraping', 'Content extraction', 'Data collection']
}
]
},
image_generation: {
title: 'Image Generation APIs',
description: 'APIs for creating and processing images',
icon: <Image size={20} />,
apis: [
{
name: 'Stability AI',
description: 'AI-powered image generation and editing',
models: ['stable-diffusion-xl', 'stable-diffusion-3'],
pricing: '$0.04 per image generated',
use_cases: ['Image generation', 'Art creation', 'Visual content']
}
]
},
embeddings: {
title: 'Embeddings & Vector APIs',
description: 'APIs for text embeddings and vector operations',
icon: <Database size={20} />,
apis: [
{
name: 'Gemini Embeddings',
description: 'Text embeddings for semantic search and analysis',
models: ['gemini-embedding'],
pricing: '$0.15 per 1M input tokens',
use_cases: ['Semantic search', 'Text similarity', 'Vector databases']
}
]
}
};
const ComprehensiveAPIBreakdown: React.FC<ComprehensiveAPIBreakdownProps> = ({
providerBreakdown,
totalCost
}) => {
// Get active providers from breakdown
const activeProviders = Object.entries(providerBreakdown)
.filter(([_, data]) => data.cost > 0)
.map(([provider, data]) => ({ provider, ...data }));
const getProviderCategory = (providerName: string) => {
const provider = providerName.toLowerCase();
if (['gemini', 'openai', 'anthropic', 'mistral'].includes(provider)) {
return 'llm_models';
}
if (['tavily', 'serper', 'metaphor'].includes(provider)) {
return 'search_apis';
}
if (['firecrawl'].includes(provider)) {
return 'content_processing';
}
if (['stability'].includes(provider)) {
return 'image_generation';
}
return 'llm_models'; // default
};
const getCategoryStats = (categoryKey: string) => {
const categoryProviders = activeProviders.filter(p =>
getProviderCategory(p.provider) === categoryKey
);
return {
count: categoryProviders.length,
totalCost: categoryProviders.reduce((sum, p) => sum + p.cost, 0),
totalCalls: categoryProviders.reduce((sum, p) => sum + p.calls, 0),
totalTokens: categoryProviders.reduce((sum, p) => sum + p.tokens, 0)
};
};
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
>
<Card
sx={{
height: '100%',
background: 'linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255,255,255,0.1)',
borderRadius: 3,
position: 'relative',
overflow: 'hidden'
}}
>
{/* Header */}
<CardContent sx={{ pb: 1 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6" sx={{ display: 'flex', alignItems: 'center', gap: 1, fontWeight: 'bold', color: '#ffffff' }}>
<BarChart3 size={20} />
Comprehensive API Breakdown
</Typography>
<Tooltip title="Detailed breakdown of all API usage across categories">
<Info size={16} color="rgba(255,255,255,0.7)" />
</Tooltip>
</Box>
</CardContent>
<CardContent sx={{ pt: 0 }}>
{/* Summary Stats */}
<Box sx={{ mb: 3, p: 2, backgroundColor: 'rgba(255,255,255,0.05)', borderRadius: 2 }}>
<Grid container spacing={2}>
<Grid item xs={6} sm={3}>
<Box sx={{ textAlign: 'center' }}>
<Typography variant="h6" sx={{ fontWeight: 'bold', color: '#ffffff' }}>
{Object.keys(API_CATEGORIES).length}
</Typography>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)' }}>
API Categories
</Typography>
</Box>
</Grid>
<Grid item xs={6} sm={3}>
<Box sx={{ textAlign: 'center' }}>
<Typography variant="h6" sx={{ fontWeight: 'bold', color: '#ffffff' }}>
{activeProviders.length}
</Typography>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)' }}>
Active Providers
</Typography>
</Box>
</Grid>
<Grid item xs={6} sm={3}>
<Box sx={{ textAlign: 'center' }}>
<Typography variant="h6" sx={{ fontWeight: 'bold', color: '#ffffff' }}>
${totalCost.toFixed(4)}
</Typography>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)' }}>
Total Cost
</Typography>
</Box>
</Grid>
<Grid item xs={6} sm={3}>
<Box sx={{ textAlign: 'center' }}>
<Typography variant="h6" sx={{ fontWeight: 'bold', color: '#ffffff' }}>
{activeProviders.reduce((sum, p) => sum + p.calls, 0)}
</Typography>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)' }}>
Total Calls
</Typography>
</Box>
</Grid>
</Grid>
</Box>
{/* API Categories */}
{Object.entries(API_CATEGORIES).map(([categoryKey, category]) => {
const stats = getCategoryStats(categoryKey);
const hasUsage = stats.count > 0;
return (
<Accordion
key={categoryKey}
sx={{
mb: 1,
backgroundColor: 'rgba(255,255,255,0.05)',
'&:before': { display: 'none' },
'&.Mui-expanded': { margin: '0 0 8px 0' }
}}
>
<AccordionSummary
expandIcon={<ExpandMore sx={{ color: 'rgba(255,255,255,0.7)' }} />}
sx={{
minHeight: 48,
'&.Mui-expanded': { minHeight: 48 }
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, width: '100%' }}>
<Box sx={{ color: hasUsage ? '#4ade80' : 'rgba(255,255,255,0.5)' }}>
{category.icon}
</Box>
<Box sx={{ flex: 1 }}>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold', color: '#ffffff' }}>
{category.title}
</Typography>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)' }}>
{category.description}
</Typography>
</Box>
{hasUsage && (
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center' }}>
<Chip
label={`${stats.count} active`}
size="small"
sx={{
backgroundColor: 'rgba(74, 222, 128, 0.2)',
color: '#4ade80',
border: '1px solid rgba(74, 222, 128, 0.3)'
}}
/>
<Typography variant="caption" sx={{ color: '#4ade80', fontWeight: 'bold' }}>
${stats.totalCost.toFixed(4)}
</Typography>
</Box>
)}
</Box>
</AccordionSummary>
<AccordionDetails sx={{ pt: 0 }}>
<Grid container spacing={2}>
{category.apis.map((api) => {
const providerData = activeProviders.find(p =>
p.provider.toLowerCase() === api.name.toLowerCase()
);
return (
<Grid item xs={12} md={6} key={api.name}>
<Box
sx={{
p: 2,
backgroundColor: providerData ? 'rgba(74, 222, 128, 0.1)' : 'rgba(255,255,255,0.05)',
borderRadius: 2,
border: providerData ? '1px solid rgba(74, 222, 128, 0.2)' : '1px solid rgba(255,255,255,0.1)'
}}
>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 1 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 'bold', color: '#ffffff' }}>
{api.name}
</Typography>
{providerData && (
<Chip
label="Active"
size="small"
sx={{
backgroundColor: 'rgba(74, 222, 128, 0.2)',
color: '#4ade80'
}}
/>
)}
</Box>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)', display: 'block', mb: 1 }}>
{api.description}
</Typography>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.6)', display: 'block', mb: 1 }}>
Pricing: {api.pricing}
</Typography>
{providerData && (
<Box sx={{ mt: 2, p: 1, backgroundColor: 'rgba(0,0,0,0.2)', borderRadius: 1 }}>
<Grid container spacing={1}>
<Grid item xs={4}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)' }}>
Cost
</Typography>
<Typography variant="caption" sx={{ display: 'block', color: '#4ade80', fontWeight: 'bold' }}>
${providerData.cost.toFixed(4)}
</Typography>
</Grid>
<Grid item xs={4}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)' }}>
Calls
</Typography>
<Typography variant="caption" sx={{ display: 'block', color: '#ffffff', fontWeight: 'bold' }}>
{providerData.calls}
</Typography>
</Grid>
<Grid item xs={4}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)' }}>
Tokens
</Typography>
<Typography variant="caption" sx={{ display: 'block', color: '#ffffff', fontWeight: 'bold' }}>
{providerData.tokens.toLocaleString()}
</Typography>
</Grid>
</Grid>
</Box>
)}
<Box sx={{ mt: 1 }}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.6)' }}>
Use cases: {api.use_cases.join(', ')}
</Typography>
</Box>
</Box>
</Grid>
);
})}
</Grid>
</AccordionDetails>
</Accordion>
);
})}
</CardContent>
</Card>
</motion.div>
);
};
export default ComprehensiveAPIBreakdown;