"use client"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, } from "recharts"; interface ModelDistributionChartProps { title: string; subtitle?: string; data: Array<{ name: string; value: number; color?: string; models?: Array<{ name: string; value: number; }>; }>; showLegend?: boolean; totalTokens?: number; } interface ModelData { name: string; value: number; } interface StackedDataEntry { provider: string; total: number; models: ModelData[]; baseColor: string; modelColors: string[]; [key: string]: string | number | ModelData[] | string[]; } interface ModelInfo { color: string; name: string; provider: string; } // Couleurs par fournisseur (couleurs de base) const providerColors: { [key: string]: string } = { Anthropic: "#7C3AED", // Violet vif OpenAI: "#059669", // Vert turquoise vif "Mistral AI": "#D97706", // Orange vif Meta: "#DB2777", // Rose/Magenta vif Google: "#2563EB", // Bleu vif Cohere: "#0891B2", // Cyan vif }; // Fonction pour générer des variations de couleur pour les modèles d'un même provider const generateModelColors = (baseColor: string, modelCount: number) => { const colors = []; for (let i = 0; i < modelCount; i++) { // Créer des variations en ajustant la luminosité const opacity = 1 - i * 0.15; // De 1.0 à 0.4 environ colors.push( `${baseColor}${Math.round(opacity * 255) .toString(16) .padStart(2, "0")}` ); } return colors; }; // Fonction pour regrouper les modèles par fournisseur et préparer les données pour le graphique empilé const prepareStackedData = ( modelData: Array<{ name: string; value: number }> ) => { const providerMap: { [key: string]: { value: number; models: Array<{ name: string; value: number }>; }; } = {}; modelData.forEach((model) => { let provider = ""; // Déterminer le fournisseur basé sur le nom du modèle if ( model.name.toLowerCase().includes("claude") || model.name.toLowerCase().includes("anthropic") ) { provider = "Anthropic"; } else if ( model.name.toLowerCase().includes("gpt") || model.name.toLowerCase().includes("openai") ) { provider = "OpenAI"; } else if (model.name.toLowerCase().includes("mistral")) { provider = "Mistral AI"; } else if ( model.name.toLowerCase().includes("llama") || model.name.toLowerCase().includes("meta") ) { provider = "Meta"; } else if ( model.name.toLowerCase().includes("palm") || model.name.toLowerCase().includes("gemini") || model.name.toLowerCase().includes("google") ) { provider = "Google"; } else if (model.name.toLowerCase().includes("cohere")) { provider = "Cohere"; } else { provider = "Autres"; } if (!providerMap[provider]) { providerMap[provider] = { value: 0, models: [] }; } providerMap[provider].value += model.value; providerMap[provider].models.push(model); }); // Créer les données pour le graphique empilé const stackedData: StackedDataEntry[] = Object.entries(providerMap).map( ([providerName, data]) => { const baseColor = providerColors[providerName] || "#6B7280"; const modelColors = generateModelColors(baseColor, data.models.length); // Créer un objet avec le provider comme clé et chaque modèle comme propriété const stackedEntry: StackedDataEntry = { provider: providerName, total: data.value, models: data.models, baseColor, modelColors, }; // Ajouter chaque modèle comme propriété séparée pour le stacking data.models.forEach((model, index) => { const modelKey = `${providerName}_${model.name}`; stackedEntry[modelKey] = model.value; stackedEntry[`${modelKey}_color`] = modelColors[index]; stackedEntry[`${modelKey}_name`] = model.name; }); return stackedEntry; } ); // Créer la liste de tous les modèles uniques pour les barres const allModelKeys: string[] = []; const modelInfo: { [key: string]: ModelInfo } = {}; stackedData.forEach((entry) => { entry.models.forEach((model, index) => { const modelKey = `${entry.provider}_${model.name}`; allModelKeys.push(modelKey); modelInfo[modelKey] = { color: entry.modelColors[index], name: model.name, provider: entry.provider, }; }); }); return { stackedData, allModelKeys, modelInfo }; }; interface CustomStackedTooltipProps { active?: boolean; payload?: Array<{ payload: StackedDataEntry; }>; label?: string; } const CustomStackedTooltip = ({ active, payload, label, }: CustomStackedTooltipProps) => { if (active && payload && payload.length) { const providerData = payload[0].payload; const totalTokens = providerData.total; return (
{label}
Total: {totalTokens.toLocaleString()} tokens
{providerData.models && (Modèles:
{providerData.models.map((model, index) => ({model.name}: {model.value.toLocaleString()} tokens
{subtitle}
)}{item.total.toLocaleString()}
{item.models.length} modèle{item.models.length > 1 ? "s" : ""}
Total général:{" "} {totalTokens.toLocaleString()} {" "} tokens