chat-with-ai/src/components/AnalyticsPanel.tsx

109 lines
4.2 KiB
TypeScript

import React, { useState } from 'react';
import { useChat } from '../contexts/ChatContext';
import { useConfig } from '../contexts/ConfigContext';
import { Button } from './ui/button';
import { AnalyticsDashboard } from './AnalyticsDashboard';
export function AnalyticsPanel() {
const { lastTokenUsage } = useChat();
const { provider, selectedModel } = useConfig();
const [isExpanded, setIsExpanded] = useState(false);
// Define pricing per million tokens for different models (in USD)
const modelPricing: {
[key: string]: {
inputPerMillion: number;
outputPerMillion: number;
currency: string;
};
} = {
// OpenAI models
'gpt-4o': { inputPerMillion: 5, outputPerMillion: 15, currency: 'USD' },
'gpt-4-turbo': { inputPerMillion: 10, outputPerMillion: 30, currency: 'USD' },
'gpt-3.5-turbo': { inputPerMillion: 0.5, outputPerMillion: 1.5, currency: 'USD' },
// Anthropic models
'claude-3-opus-20240229': { inputPerMillion: 15, outputPerMillion: 75, currency: 'USD' },
'claude-3-sonnet-20240229': { inputPerMillion: 3, outputPerMillion: 15, currency: 'USD' },
'claude-3-haiku-20240307': { inputPerMillion: 0.25, outputPerMillion: 1.25, currency: 'USD' },
// Google models
'gemini-pro': { inputPerMillion: 0.25, outputPerMillion: 0.25, currency: 'USD' },
'gemini-1.5-pro': { inputPerMillion: 3.5, outputPerMillion: 10.5, currency: 'USD' },
// Default for unknown models
'default': { inputPerMillion: 1, outputPerMillion: 3, currency: 'USD' }
};
if (!lastTokenUsage) return null;
const { prompt, completion, total } = lastTokenUsage;
// Get pricing for the selected model, or use default
const pricing = modelPricing[selectedModel] || modelPricing.default;
const { inputPerMillion, outputPerMillion, currency } = pricing;
// Calculate costs
const promptCost = (prompt / 1000000) * inputPerMillion;
const completionCost = (completion / 1000000) * outputPerMillion;
const totalCost = promptCost + completionCost;
return (
<div className="p-2 border-t space-y-2">
<div className="flex justify-between items-center">
<div className="flex items-center gap-2">
<div className="text-xs font-medium">Token Usage</div>
<Button
variant="ghost"
size="sm"
className="h-6 px-2 text-xs"
onClick={() => setIsExpanded(!isExpanded)}
>
{isExpanded ? 'Hide Details' : 'Show Details'}
</Button>
<AnalyticsDashboard />
</div>
<div className="text-xs text-muted-foreground">
Total: {total.toLocaleString()} tokens
</div>
</div>
{isExpanded && (
<div className="bg-muted/30 rounded-md p-3 space-y-3 text-sm">
<div className="grid grid-cols-2 gap-2">
<div className="space-y-1">
<h4 className="text-xs font-medium">Last Message</h4>
<div className="grid grid-cols-2 gap-1 text-xs">
<div>Input:</div>
<div className="text-right">{prompt.toLocaleString()} tokens</div>
<div>Output:</div>
<div className="text-right">{completion.toLocaleString()} tokens</div>
<div>Total:</div>
<div className="text-right">{total.toLocaleString()} tokens</div>
</div>
</div>
<div className="space-y-1">
<h4 className="text-xs font-medium">Estimated Cost</h4>
<div className="grid grid-cols-2 gap-1 text-xs">
<div>Input:</div>
<div className="text-right">{currency} {promptCost.toFixed(6)}</div>
<div>Output:</div>
<div className="text-right">{currency} {completionCost.toFixed(6)}</div>
<div>Total:</div>
<div className="text-right">{currency} {totalCost.toFixed(6)}</div>
</div>
</div>
</div>
<div className="text-xs text-muted-foreground mt-2">
<p>Pricing: {currency} {inputPerMillion} / {currency} {outputPerMillion} per million tokens (input/output)</p>
<p className="mt-1">Note: View detailed analytics by clicking the Analytics button.</p>
</div>
</div>
)}
</div>
);
}