Add files via upload

dashboard polling removed in favour for SSE pub:sub
This commit is contained in:
Alpha Nerd 2025-09-05 09:44:35 +02:00 committed by GitHub
parent ef936bb2a0
commit a23ccafc5a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 115 additions and 15 deletions

View file

@ -253,7 +253,7 @@ async function loadPS(){
/* ---------- Usage Chart (stackedpercentage) ---------- */
function getColor(seed){
const h = Math.abs(hashString(seed) % 360);
return `hsl(${h}, 70%, 50%)`;
return `hsl(${h}, 80%, 40%)`;
}
function hashString(str){
let hash = 0;
@ -263,28 +263,57 @@ function hashString(str){
}
return Math.abs(hash);
}
async function loadUsage(){
try{
const data = await fetchJSON('/api/usage');
async function loadUsage() {
// Create the EventSource once and keep it around
const source = new EventSource('/api/usage-stream');
// -----------------------------------------------------------------
// Helper that receives the payload and renders the chart
// -----------------------------------------------------------------
const renderChart = (data) => {
const chart = document.getElementById('usage-chart');
const usage = data.usage_counts || {};
let html = '';
for (const [endpoint, models] of Object.entries(usage)){
const total = Object.values(models).reduce((a,b)=>a+b,0);
html += `<div class="endpoint-bar"><div class="endpoint-label">${endpoint}</div><div class="bar">`;
for (const [model, count] of Object.entries(models)){
const pct = total ? (count/total)*100 : 0;
for (const [endpoint, models] of Object.entries(usage)) {
const total = Object.values(models).reduce((a, b) => a + b, 0);
html += `<div class="endpoint-bar">
<div class="endpoint-label">${endpoint}</div>
<div class="bar">`;
for (const [model, count] of Object.entries(models)) {
const pct = total ? (count / total) * 100 : 0;
const width = pct.toFixed(2);
const color = getColor(model);
html += `<div class="segment" style="width:${width}%;background:${color};">${model} (${count})</div>`;
html += `<div class="segment"
style="width:${width}%;background:${color};">
${model} (${count})
</div>`;
}
html += `</div></div>`;
}
chart.innerHTML = html;
}catch(e){
console.error('Failed to load usage counts', e);
}
};
// -----------------------------------------------------------------
// Event handlers
// -----------------------------------------------------------------
source.onmessage = (e) => {
try {
const payload = JSON.parse(e.data); // SSE sends plain text
renderChart(payload);
} catch (err) {
console.error('Failed to parse SSE payload', err);
}
};
source.onerror = (err) => {
console.error('SSE connection error. Retrying...', err);
// EventSource will automatically try to reconnect.
};
window.addEventListener('beforeunload', () => source.close());
}
/* ---------- Init ---------- */
@ -294,7 +323,7 @@ window.addEventListener('load', ()=>{
loadPS();
loadUsage();
setInterval(loadPS, 60_000);
setInterval(loadUsage, 1_000);
setInterval(loadEndpoints, 300_000);
});
</script>