Files
openproxy/dashboard/components/ModelBreakdown.tsx
Claude b88fc8ead7 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.
2025-11-19 00:04:28 +00:00

100 lines
2.5 KiB
TypeScript

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',
},
};