Skip to content

Commit 769d17d

Browse files
committed
perf(site): add LRU caches for request content
1 parent 2c0be20 commit 769d17d

1 file changed

Lines changed: 115 additions & 24 deletions

File tree

site/script.js

Lines changed: 115 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,38 @@ async function fetchJsonSafe(url) {
12621262
}
12631263
}
12641264

1265+
// ===== LRU Cache for Request Content =====
1266+
class RequestCache {
1267+
constructor(maxSize = 20) {
1268+
this.maxSize = maxSize;
1269+
this.cache = new Map();
1270+
}
1271+
1272+
get(key) {
1273+
if (!this.cache.has(key)) return null;
1274+
const value = this.cache.get(key);
1275+
this.cache.delete(key);
1276+
this.cache.set(key, value); // Move to end (most recently used)
1277+
return value;
1278+
}
1279+
1280+
set(key, value) {
1281+
if (this.cache.has(key)) {
1282+
this.cache.delete(key);
1283+
} else if (this.cache.size >= this.maxSize) {
1284+
const firstKey = this.cache.keys().next().value;
1285+
this.cache.delete(firstKey);
1286+
}
1287+
this.cache.set(key, value);
1288+
}
1289+
1290+
has(key) {
1291+
return this.cache.has(key);
1292+
}
1293+
}
1294+
1295+
const requestContentCache = new RequestCache(20);
1296+
12651297
function openRunViewer({
12661298
basePath,
12671299
vendor,
@@ -1355,7 +1387,7 @@ async function discoverTotalRequests(state) {
13551387
return maxFound;
13561388
}
13571389

1358-
async function loadAndRenderRequest(state) {
1390+
async function loadAndRenderRequest(state, prefetch = false) {
13591391
const {
13601392
basePath,
13611393
vendor,
@@ -1367,15 +1399,45 @@ async function loadAndRenderRequest(state) {
13671399
} = state;
13681400
const reqId = formatRequestId(index);
13691401
const runBase = buildRequestBasePath(basePath, vendor, model, runId, reqId, strategy);
1402+
const cacheKey = `${runId}:${index}`;
1403+
1404+
let content;
1405+
const cached = requestContentCache.get(cacheKey);
13701406

1371-
const [reasoning, toolcall, strategyMd, gamestateMd, memoryMd, metadata] = await Promise.all([
1372-
fetchTextSafe(`${runBase}/reasoning.md`),
1373-
fetchJsonSafe(`${runBase}/tool_call.json`),
1374-
fetchTextSafe(`${runBase}/strategy.md`),
1375-
fetchTextSafe(`${runBase}/gamestate.md`),
1376-
fetchTextSafe(`${runBase}/memory.md`),
1377-
fetchJsonSafe(`${runBase}/metadata.json`)
1378-
]);
1407+
if (cached) {
1408+
content = cached;
1409+
} else {
1410+
const [reasoning, toolcall, strategyMd, gamestateMd, memoryMd, metadata] = await Promise.all([
1411+
fetchTextSafe(`${runBase}/reasoning.md`),
1412+
fetchJsonSafe(`${runBase}/tool_call.json`),
1413+
fetchTextSafe(`${runBase}/strategy.md`),
1414+
fetchTextSafe(`${runBase}/gamestate.md`),
1415+
fetchTextSafe(`${runBase}/memory.md`),
1416+
fetchJsonSafe(`${runBase}/metadata.json`)
1417+
]);
1418+
content = {
1419+
reasoning,
1420+
toolcall,
1421+
strategyMd,
1422+
gamestateMd,
1423+
memoryMd,
1424+
metadata,
1425+
runBase
1426+
};
1427+
requestContentCache.set(cacheKey, content);
1428+
}
1429+
1430+
// If prefetching, just cache - don't render
1431+
if (prefetch) return;
1432+
1433+
const {
1434+
reasoning,
1435+
toolcall,
1436+
strategyMd,
1437+
gamestateMd,
1438+
memoryMd,
1439+
metadata
1440+
} = content;
13791441

13801442
// Discover total requests for this run if not cached
13811443
if (!state.totalRequests[runId]) {
@@ -1416,26 +1478,30 @@ async function loadAndRenderRequest(state) {
14161478
overlay.querySelector('#run-title').innerHTML = title;
14171479

14181480
const imgEl = overlay.querySelector('#run-screenshot');
1419-
// Try formats in order: webp -> png -> avif
1420-
const formats = ['webp', 'png', 'avif'];
1421-
let formatIndex = 0;
14221481

1423-
const tryNextFormat = () => {
1424-
if (formatIndex < formats.length) {
1425-
imgEl.src = `${runBase}/screenshot.${formats[formatIndex]}`;
1426-
formatIndex++;
1482+
// Parallel format detection using HEAD requests
1483+
const formats = ['webp', 'png', 'avif'];
1484+
const formatProbes = formats.map(async (format) => {
1485+
const url = `${content.runBase}/screenshot.${format}`;
1486+
try {
1487+
const response = await fetch(url, {
1488+
method: 'HEAD'
1489+
});
1490+
if (response.ok) return url;
1491+
} catch {
1492+
/* format unavailable */
14271493
}
1428-
};
1494+
return null;
1495+
});
14291496

1430-
imgEl.onerror = () => {
1431-
if (formatIndex < formats.length) {
1432-
tryNextFormat();
1497+
Promise.all(formatProbes).then(results => {
1498+
const availableUrl = results.find(r => r !== null);
1499+
if (availableUrl) {
1500+
imgEl.src = availableUrl;
14331501
} else {
1434-
imgEl.onerror = null;
1502+
imgEl.alt = 'Screenshot not available';
14351503
}
1436-
};
1437-
1438-
tryNextFormat();
1504+
});
14391505

14401506
overlay.querySelector('#run-strategy').textContent = strategyMd || '(No strategy.md)';
14411507
overlay.querySelector('#run-gamestate').textContent = gamestateMd || '(No gamestate.md)';
@@ -1481,6 +1547,31 @@ async function loadAndRenderRequest(state) {
14811547
toolCallDiv.textContent = `${name}(${argsString})`;
14821548
}
14831549

1550+
// Prefetch adjacent requests in background
1551+
prefetchAdjacentRequests(state);
1552+
}
1553+
1554+
function prefetchAdjacentRequests(state) {
1555+
const {
1556+
runId,
1557+
index
1558+
} = state;
1559+
1560+
// Prefetch previous
1561+
if (index > 1 && !requestContentCache.has(`${runId}:${index - 1}`)) {
1562+
loadAndRenderRequest({
1563+
...state,
1564+
index: index - 1
1565+
}, true).catch(() => {});
1566+
}
1567+
1568+
// Prefetch next
1569+
if (!requestContentCache.has(`${runId}:${index + 1}`)) {
1570+
loadAndRenderRequest({
1571+
...state,
1572+
index: index + 1
1573+
}, true).catch(() => {});
1574+
}
14841575
}
14851576

14861577
async function navigateRun(state, delta) {

0 commit comments

Comments
 (0)