Restore to commit 74e578279624c6045ca440a3459ebfa1f8d54191
This commit is contained in:
328
chat/public/test_token_usage.html
Normal file
328
chat/public/test_token_usage.html
Normal file
@@ -0,0 +1,328 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Token Usage Test</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 40px auto;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.card {
|
||||
background: white;
|
||||
padding: 24px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
h1 {
|
||||
margin: 0 0 20px;
|
||||
color: #333;
|
||||
}
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
input[type="number"] {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
flex: 1;
|
||||
min-width: 150px;
|
||||
}
|
||||
button {
|
||||
padding: 8px 16px;
|
||||
background: #008060;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
}
|
||||
button:hover {
|
||||
background: #006e51;
|
||||
}
|
||||
button:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.usage-meter {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.usage-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
.usage-bar {
|
||||
height: 24px;
|
||||
background: #e0e0e0;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.usage-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #008060, #00a572);
|
||||
transition: width 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.status {
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
margin-top: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.status.success {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
.status.error {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
.status.info {
|
||||
background: #d1ecf1;
|
||||
color: #0c5460;
|
||||
border: 1px solid #bee5eb;
|
||||
}
|
||||
.stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 16px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.stat {
|
||||
background: #f8f9fa;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.stat-value {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
pre {
|
||||
background: #f8f9fa;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
overflow-x: auto;
|
||||
font-size: 12px;
|
||||
margin: 12px 0 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h1>Token Usage Test</h1>
|
||||
<p>Test the token usage tracking and progress bar updates. This simulates token consumption without actually running AI models.</p>
|
||||
|
||||
<div class="controls">
|
||||
<input type="number" id="tokenAmount" value="1000" min="1" step="100" placeholder="Tokens to add">
|
||||
<button id="simulateBtn">Simulate Token Usage</button>
|
||||
<button id="refreshBtn">Refresh Usage</button>
|
||||
</div>
|
||||
|
||||
<div class="usage-meter">
|
||||
<div class="usage-header">
|
||||
<span>Monthly Token Usage</span>
|
||||
<span id="usagePercent">—</span>
|
||||
</div>
|
||||
<div class="usage-bar">
|
||||
<div class="usage-fill" id="usageFill" style="width: 0%">
|
||||
<span id="usageText">0%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat">
|
||||
<div class="stat-label">Used</div>
|
||||
<div class="stat-value" id="usedTokens">—</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-label">Limit</div>
|
||||
<div class="stat-value" id="limitTokens">—</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-label">Remaining</div>
|
||||
<div class="stat-value" id="remainingTokens">—</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-label">Plan</div>
|
||||
<div class="stat-value" id="planName">—</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="statusMessage" style="display: none;"></div>
|
||||
<pre id="debugOutput" style="display: none;"></pre>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const tokenAmountInput = document.getElementById('tokenAmount');
|
||||
const simulateBtn = document.getElementById('simulateBtn');
|
||||
const refreshBtn = document.getElementById('refreshBtn');
|
||||
const usageFill = document.getElementById('usageFill');
|
||||
const usageText = document.getElementById('usageText');
|
||||
const usagePercent = document.getElementById('usagePercent');
|
||||
const usedTokens = document.getElementById('usedTokens');
|
||||
const limitTokens = document.getElementById('limitTokens');
|
||||
const remainingTokens = document.getElementById('remainingTokens');
|
||||
const planName = document.getElementById('planName');
|
||||
const statusMessage = document.getElementById('statusMessage');
|
||||
const debugOutput = document.getElementById('debugOutput');
|
||||
|
||||
function showStatus(message, type = 'info') {
|
||||
statusMessage.textContent = message;
|
||||
statusMessage.className = `status ${type}`;
|
||||
statusMessage.style.display = 'block';
|
||||
setTimeout(() => {
|
||||
statusMessage.style.display = 'none';
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function updateUsageDisplay(summary) {
|
||||
if (!summary) {
|
||||
showStatus('No usage data available', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const used = summary.used || 0;
|
||||
const limit = summary.limit || 0;
|
||||
const percent = summary.percent || 0;
|
||||
const remaining = summary.remaining || 0;
|
||||
const plan = summary.plan || 'unknown';
|
||||
|
||||
// Update progress bar
|
||||
usageFill.style.width = `${Math.min(100, percent)}%`;
|
||||
usageText.textContent = `${percent}%`;
|
||||
usagePercent.textContent = `${percent}% used`;
|
||||
|
||||
// Update stats
|
||||
usedTokens.textContent = used.toLocaleString();
|
||||
limitTokens.textContent = limit.toLocaleString();
|
||||
remainingTokens.textContent = remaining.toLocaleString();
|
||||
planName.textContent = plan.charAt(0).toUpperCase() + plan.slice(1);
|
||||
|
||||
// Change color based on usage
|
||||
if (percent >= 90) {
|
||||
usageFill.style.background = 'linear-gradient(90deg, #dc3545, #c82333)';
|
||||
} else if (percent >= 75) {
|
||||
usageFill.style.background = 'linear-gradient(90deg, #ffc107, #e0a800)';
|
||||
} else {
|
||||
usageFill.style.background = 'linear-gradient(90deg, #008060, #00a572)';
|
||||
}
|
||||
|
||||
debugOutput.textContent = JSON.stringify(summary, null, 2);
|
||||
debugOutput.style.display = 'block';
|
||||
}
|
||||
|
||||
async function fetchUsage() {
|
||||
try {
|
||||
const response = await fetch('/api/account/usage?_t=' + Date.now());
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
console.log('[TEST] Usage data received:', data);
|
||||
return data.summary;
|
||||
} catch (error) {
|
||||
console.error('[TEST] Failed to fetch usage:', error);
|
||||
showStatus(`Failed to fetch usage: ${error.message}`, 'error');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function simulateTokenUsage() {
|
||||
const tokens = parseInt(tokenAmountInput.value) || 1000;
|
||||
|
||||
simulateBtn.disabled = true;
|
||||
simulateBtn.textContent = 'Simulating...';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/test/simulate-tokens', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ tokens }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('[TEST] Simulation response:', data);
|
||||
|
||||
showStatus(`✓ Simulated ${tokens.toLocaleString()} tokens successfully`, 'success');
|
||||
|
||||
// Update display with returned summary
|
||||
if (data.summary) {
|
||||
updateUsageDisplay(data.summary);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[TEST] Failed to simulate:', error);
|
||||
showStatus(`Failed to simulate: ${error.message}`, 'error');
|
||||
} finally {
|
||||
simulateBtn.disabled = false;
|
||||
simulateBtn.textContent = 'Simulate Token Usage';
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshUsage() {
|
||||
refreshBtn.disabled = true;
|
||||
refreshBtn.textContent = 'Refreshing...';
|
||||
|
||||
try {
|
||||
const summary = await fetchUsage();
|
||||
if (summary) {
|
||||
updateUsageDisplay(summary);
|
||||
showStatus('Usage data refreshed', 'success');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[TEST] Refresh failed:', error);
|
||||
} finally {
|
||||
refreshBtn.disabled = false;
|
||||
refreshBtn.textContent = 'Refresh Usage';
|
||||
}
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
simulateBtn.addEventListener('click', simulateTokenUsage);
|
||||
refreshBtn.addEventListener('click', refreshUsage);
|
||||
|
||||
// Load initial usage on page load
|
||||
window.addEventListener('DOMContentLoaded', async () => {
|
||||
console.log('[TEST] Page loaded, fetching initial usage...');
|
||||
const summary = await fetchUsage();
|
||||
if (summary) {
|
||||
updateUsageDisplay(summary);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user