mirror of
https://github.com/praveentcom/openproxy.git
synced 2026-07-13 07:36:40 +02:00
feat: add Next.js metrics dashboard for real-time visualization
Add a lightweight Next.js dashboard to visualize OpenProxy metrics in real-time. The dashboard provides comprehensive insights into LLM API usage, costs, and performance. Features: - Real-time metrics overview (requests, tokens, costs, response times) - Model breakdown with usage statistics - Hourly trends visualization with charts - Recent requests table with detailed information - Auto-refresh every 30 seconds - Configurable time ranges (1h, 6h, 24h, 7d) Technical details: - Built with Next.js 14 and React 18 - Uses Recharts for data visualization - Connects directly to PostgreSQL database - Runs on port 3008 by default - TypeScript for type safety - Minimal dependencies for lightweight deployment The dashboard complements the proxy server by providing a user-friendly interface for monitoring and analyzing LLM API usage patterns.
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
interface MetricsOverviewProps {
|
||||
summary: {
|
||||
totalRequests: number;
|
||||
totalTokens: number;
|
||||
totalCost: number;
|
||||
avgResponseTime: number;
|
||||
uniqueModels: number;
|
||||
uniqueClients: number;
|
||||
};
|
||||
}
|
||||
|
||||
export default function MetricsOverview({ summary }: MetricsOverviewProps) {
|
||||
const metrics = [
|
||||
{
|
||||
label: 'Total Requests',
|
||||
value: summary.totalRequests.toLocaleString(),
|
||||
icon: '📊',
|
||||
},
|
||||
{
|
||||
label: 'Total Tokens',
|
||||
value: summary.totalTokens.toLocaleString(),
|
||||
icon: '🔢',
|
||||
},
|
||||
{
|
||||
label: 'Total Cost',
|
||||
value: `$${summary.totalCost.toFixed(4)}`,
|
||||
icon: '💰',
|
||||
},
|
||||
{
|
||||
label: 'Avg Response Time',
|
||||
value: `${Math.round(summary.avgResponseTime)}ms`,
|
||||
icon: '⚡',
|
||||
},
|
||||
{
|
||||
label: 'Unique Models',
|
||||
value: summary.uniqueModels.toString(),
|
||||
icon: '🤖',
|
||||
},
|
||||
{
|
||||
label: 'Unique Clients',
|
||||
value: summary.uniqueClients.toString(),
|
||||
icon: '👥',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
<h2 style={styles.title}>Overview</h2>
|
||||
<div style={styles.grid}>
|
||||
{metrics.map((metric) => (
|
||||
<div key={metric.label} style={styles.card}>
|
||||
<div style={styles.icon}>{metric.icon}</div>
|
||||
<div style={styles.content}>
|
||||
<div style={styles.label}>{metric.label}</div>
|
||||
<div style={styles.value}>{metric.value}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
marginBottom: '2rem',
|
||||
},
|
||||
title: {
|
||||
fontSize: '1.5rem',
|
||||
marginBottom: '1.5rem',
|
||||
color: '#2c3e50',
|
||||
},
|
||||
grid: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))',
|
||||
gap: '1rem',
|
||||
},
|
||||
card: {
|
||||
backgroundColor: '#fff',
|
||||
padding: '1.5rem',
|
||||
borderRadius: '8px',
|
||||
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
|
||||
display: 'flex',
|
||||
gap: '1rem',
|
||||
alignItems: 'center',
|
||||
},
|
||||
icon: {
|
||||
fontSize: '2rem',
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
},
|
||||
label: {
|
||||
fontSize: '0.9rem',
|
||||
color: '#7f8c8d',
|
||||
marginBottom: '0.25rem',
|
||||
},
|
||||
value: {
|
||||
fontSize: '1.5rem',
|
||||
fontWeight: 600,
|
||||
color: '#2c3e50',
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user