mirror of
https://github.com/praveentcom/openproxy.git
synced 2026-05-26 17:47:57 +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,99 @@
|
||||
interface ModelBreakdownProps {
|
||||
models: {
|
||||
model: string;
|
||||
request_count: string;
|
||||
total_tokens: string;
|
||||
total_cost: string;
|
||||
avg_response_time: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export default function ModelBreakdown({ models }: ModelBreakdownProps) {
|
||||
if (!models || models.length === 0) {
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
<h2 style={styles.title}>Model Breakdown</h2>
|
||||
<div style={styles.card}>
|
||||
<p style={styles.noData}>No model data available</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
<h2 style={styles.title}>Model Breakdown</h2>
|
||||
<div style={styles.card}>
|
||||
<table style={styles.table}>
|
||||
<thead>
|
||||
<tr style={styles.headerRow}>
|
||||
<th style={styles.th}>Model</th>
|
||||
<th style={styles.th}>Requests</th>
|
||||
<th style={styles.th}>Total Tokens</th>
|
||||
<th style={styles.th}>Total Cost</th>
|
||||
<th style={styles.th}>Avg Response Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{models.map((model) => (
|
||||
<tr key={model.model} style={styles.row}>
|
||||
<td style={styles.td}>
|
||||
<strong>{model.model}</strong>
|
||||
</td>
|
||||
<td style={styles.td}>{parseInt(model.request_count).toLocaleString()}</td>
|
||||
<td style={styles.td}>{parseInt(model.total_tokens).toLocaleString()}</td>
|
||||
<td style={styles.td}>${parseFloat(model.total_cost).toFixed(4)}</td>
|
||||
<td style={styles.td}>{Math.round(parseFloat(model.avg_response_time))}ms</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
marginBottom: '2rem',
|
||||
},
|
||||
title: {
|
||||
fontSize: '1.5rem',
|
||||
marginBottom: '1.5rem',
|
||||
color: '#2c3e50',
|
||||
},
|
||||
card: {
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: '8px',
|
||||
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
table: {
|
||||
width: '100%',
|
||||
borderCollapse: 'collapse' as const,
|
||||
},
|
||||
headerRow: {
|
||||
backgroundColor: '#f8f9fa',
|
||||
},
|
||||
th: {
|
||||
padding: '1rem',
|
||||
textAlign: 'left' as const,
|
||||
fontSize: '0.9rem',
|
||||
fontWeight: 600,
|
||||
color: '#2c3e50',
|
||||
borderBottom: '2px solid #e9ecef',
|
||||
},
|
||||
row: {
|
||||
borderBottom: '1px solid #e9ecef',
|
||||
},
|
||||
td: {
|
||||
padding: '1rem',
|
||||
fontSize: '0.9rem',
|
||||
color: '#495057',
|
||||
},
|
||||
noData: {
|
||||
padding: '2rem',
|
||||
textAlign: 'center' as const,
|
||||
color: '#7f8c8d',
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user