Express.js + Chart.js dashboard for LibreChat usage analytics. Queries MongoDB transactions collection for model/agent usage, costs, and top users. Dark theme with Montserrat font, black/gold (#FFC407) color scheme. Docker-ready with API key authentication. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
178 lines
6.4 KiB
HTML
178 lines
6.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>LibreChat Analytics</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="css/style.css">
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
|
<script src="https://unpkg.com/lucide@latest"></script>
|
|
</head>
|
|
<body>
|
|
<div id="app">
|
|
<!-- Sidebar -->
|
|
<nav class="sidebar">
|
|
<div class="sidebar-header">
|
|
<svg class="logo-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/>
|
|
<path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/>
|
|
<path d="M18 12a2 2 0 0 0 0 4h4v-4Z"/>
|
|
</svg>
|
|
<span class="logo-text">Analytics</span>
|
|
</div>
|
|
<ul class="nav-links">
|
|
<li class="active" data-tab="overview">
|
|
<i data-lucide="bar-chart-3" style="width:20px;height:20px"></i>
|
|
Overview
|
|
</li>
|
|
<li data-tab="users">
|
|
<i data-lucide="users" style="width:20px;height:20px"></i>
|
|
Users
|
|
</li>
|
|
<li data-tab="models">
|
|
<i data-lucide="cpu" style="width:20px;height:20px"></i>
|
|
Models
|
|
</li>
|
|
<li data-tab="agents">
|
|
<i data-lucide="bot" style="width:20px;height:20px"></i>
|
|
Agents
|
|
</li>
|
|
</ul>
|
|
<div class="sidebar-footer">
|
|
Auto-refresh: 60s
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Main Content -->
|
|
<main class="content">
|
|
<div class="top-bar">
|
|
<h1>LibreChat Analytics</h1>
|
|
</div>
|
|
|
|
<!-- Filter Bar -->
|
|
<div class="filter-bar">
|
|
<div class="period-selector">
|
|
<button class="period-btn active" data-period="24h">24H</button>
|
|
<button class="period-btn" data-period="7d">7D</button>
|
|
<button class="period-btn" data-period="30d">30D</button>
|
|
<button class="period-btn" data-period="custom">Custom</button>
|
|
</div>
|
|
<div class="date-range-picker" id="dateRangePicker" style="display:none">
|
|
<input type="date" id="startDate">
|
|
<span class="date-separator">to</span>
|
|
<input type="date" id="endDate">
|
|
</div>
|
|
<span class="refresh-info" id="lastRefresh"></span>
|
|
</div>
|
|
|
|
<!-- Overview Tab -->
|
|
<div class="tab-content active" id="tab-overview">
|
|
<div class="stats-grid" id="summaryCards">
|
|
<div class="stat-card">
|
|
<div class="stat-icon cost"><i data-lucide="dollar-sign"></i></div>
|
|
<div><span class="stat-label">Total Cost</span><span class="stat-value" id="totalCost">--</span></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon tokens"><i data-lucide="zap"></i></div>
|
|
<div><span class="stat-label">Total Tokens</span><span class="stat-value" id="totalTokens">--</span></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon users"><i data-lucide="users"></i></div>
|
|
<div><span class="stat-label">Active Users</span><span class="stat-value" id="activeUsers">--</span></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon convos"><i data-lucide="message-square"></i></div>
|
|
<div><span class="stat-label">Conversations</span><span class="stat-value" id="conversations">--</span></div>
|
|
</div>
|
|
</div>
|
|
<div class="charts-grid">
|
|
<div class="chart-container chart-wide">
|
|
<h3>Usage Over Time</h3>
|
|
<canvas id="usageChart"></canvas>
|
|
</div>
|
|
<div class="chart-container">
|
|
<h3>Cost by Model</h3>
|
|
<canvas id="costByModelChart"></canvas>
|
|
</div>
|
|
<div class="chart-container">
|
|
<h3>Cost Breakdown (Input vs Output)</h3>
|
|
<canvas id="costBreakdownChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Users Tab -->
|
|
<div class="tab-content" id="tab-users">
|
|
<div class="table-container">
|
|
<div class="table-header"><h3>Top Users by Cost</h3></div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Name</th>
|
|
<th>Email</th>
|
|
<th class="text-right">Tokens</th>
|
|
<th class="text-right">Cost</th>
|
|
<th class="text-right">Conversations</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="usersTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Models Tab -->
|
|
<div class="tab-content" id="tab-models">
|
|
<div class="table-container">
|
|
<div class="table-header"><h3>Top Models by Cost</h3></div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Model</th>
|
|
<th class="text-right">Prompt Tokens</th>
|
|
<th class="text-right">Completion Tokens</th>
|
|
<th class="text-right">Prompt Cost</th>
|
|
<th class="text-right">Completion Cost</th>
|
|
<th class="text-right">Total Cost</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="modelsTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
<div class="charts-grid">
|
|
<div class="chart-container chart-wide">
|
|
<h3>Model Cost Comparison (Input vs Output)</h3>
|
|
<canvas id="modelCostChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Agents Tab -->
|
|
<div class="tab-content" id="tab-agents">
|
|
<div class="table-container">
|
|
<div class="table-header"><h3>Top Agents by Cost</h3></div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Agent Name</th>
|
|
<th>Underlying Model</th>
|
|
<th>Provider</th>
|
|
<th class="text-right">Tokens</th>
|
|
<th class="text-right">Cost</th>
|
|
<th class="text-right">Conversations</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="agentsTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<script src="js/app.js"></script>
|
|
</body>
|
|
</html>
|