Merge pull request #58 from wagesj45/h43lwv-codex/add-timing-stats-to-maintenance-tab
Add run time stats in options
This commit is contained in:
commit
9944d78ed7
4 changed files with 90 additions and 6 deletions
|
@ -19,6 +19,8 @@ let queue = Promise.resolve();
|
||||||
let queuedCount = 0;
|
let queuedCount = 0;
|
||||||
let processing = false;
|
let processing = false;
|
||||||
let iconTimer = null;
|
let iconTimer = null;
|
||||||
|
let timingStats = { count: 0, mean: 0, m2: 0, total: 0 };
|
||||||
|
let currentStart = 0;
|
||||||
|
|
||||||
function setIcon(path) {
|
function setIcon(path) {
|
||||||
if (browser.browserAction) {
|
if (browser.browserAction) {
|
||||||
|
@ -106,6 +108,7 @@ async function applyAiRules(idsInput) {
|
||||||
updateActionIcon();
|
updateActionIcon();
|
||||||
queue = queue.then(async () => {
|
queue = queue.then(async () => {
|
||||||
processing = true;
|
processing = true;
|
||||||
|
currentStart = Date.now();
|
||||||
queuedCount--;
|
queuedCount--;
|
||||||
updateActionIcon();
|
updateActionIcon();
|
||||||
try {
|
try {
|
||||||
|
@ -141,9 +144,27 @@ async function applyAiRules(idsInput) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
processing = false;
|
processing = false;
|
||||||
|
const elapsed = Date.now() - currentStart;
|
||||||
|
currentStart = 0;
|
||||||
|
const t = timingStats;
|
||||||
|
t.count += 1;
|
||||||
|
t.total += elapsed;
|
||||||
|
const delta = elapsed - t.mean;
|
||||||
|
t.mean += delta / t.count;
|
||||||
|
t.m2 += delta * (elapsed - t.mean);
|
||||||
|
await storage.local.set({ classifyStats: t });
|
||||||
showTransientIcon("resources/img/done.png");
|
showTransientIcon("resources/img/done.png");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
processing = false;
|
processing = false;
|
||||||
|
const elapsed = Date.now() - currentStart;
|
||||||
|
currentStart = 0;
|
||||||
|
const t = timingStats;
|
||||||
|
t.count += 1;
|
||||||
|
t.total += elapsed;
|
||||||
|
const delta = elapsed - t.mean;
|
||||||
|
t.mean += delta / t.count;
|
||||||
|
t.m2 += delta * (elapsed - t.mean);
|
||||||
|
await storage.local.set({ classifyStats: t });
|
||||||
logger.aiLog("failed to apply AI rules", { level: 'error' }, e);
|
logger.aiLog("failed to apply AI rules", { level: 'error' }, e);
|
||||||
showTransientIcon("resources/img/error.png");
|
showTransientIcon("resources/img/error.png");
|
||||||
}
|
}
|
||||||
|
@ -199,6 +220,10 @@ async function clearCacheForMessages(idsInput) {
|
||||||
logger.setDebug(store.debugLogging);
|
logger.setDebug(store.debugLogging);
|
||||||
await AiClassifier.setConfig(store);
|
await AiClassifier.setConfig(store);
|
||||||
await AiClassifier.init();
|
await AiClassifier.init();
|
||||||
|
const savedStats = await storage.local.get('classifyStats');
|
||||||
|
if (savedStats.classifyStats && typeof savedStats.classifyStats === 'object') {
|
||||||
|
Object.assign(timingStats, savedStats.classifyStats);
|
||||||
|
}
|
||||||
aiRules = Array.isArray(store.aiRules) ? store.aiRules.map(r => {
|
aiRules = Array.isArray(store.aiRules) ? store.aiRules.map(r => {
|
||||||
if (r.actions) return r;
|
if (r.actions) return r;
|
||||||
const actions = [];
|
const actions = [];
|
||||||
|
@ -390,6 +415,16 @@ async function clearCacheForMessages(idsInput) {
|
||||||
}
|
}
|
||||||
} else if (msg?.type === "sortana:getQueueCount") {
|
} else if (msg?.type === "sortana:getQueueCount") {
|
||||||
return { count: queuedCount + (processing ? 1 : 0) };
|
return { count: queuedCount + (processing ? 1 : 0) };
|
||||||
|
} else if (msg?.type === "sortana:getTiming") {
|
||||||
|
const t = timingStats;
|
||||||
|
const std = t.count > 1 ? Math.sqrt(t.m2 / (t.count - 1)) : 0;
|
||||||
|
return {
|
||||||
|
count: queuedCount + (processing ? 1 : 0),
|
||||||
|
current: currentStart ? Date.now() - currentStart : -1,
|
||||||
|
average: t.mean,
|
||||||
|
total: t.total,
|
||||||
|
stddev: std
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
logger.aiLog("Unknown message type, ignoring", {level: 'warn'}, msg?.type);
|
logger.aiLog("Unknown message type, ignoring", {level: 'warn'}, msg?.type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -323,6 +323,13 @@ async function clearCache() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getCacheSize() {
|
||||||
|
if (!gCacheLoaded) {
|
||||||
|
await loadCache();
|
||||||
|
}
|
||||||
|
return gCache.size;
|
||||||
|
}
|
||||||
|
|
||||||
function classifyTextSync(text, criterion, cacheKey = null) {
|
function classifyTextSync(text, criterion, cacheKey = null) {
|
||||||
if (!Services?.tm?.spinEventLoopUntil) {
|
if (!Services?.tm?.spinEventLoopUntil) {
|
||||||
throw new Error("classifyTextSync requires Services");
|
throw new Error("classifyTextSync requires Services");
|
||||||
|
@ -407,4 +414,4 @@ async function init() {
|
||||||
await loadCache();
|
await loadCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
export { classifyText, classifyTextSync, setConfig, removeCacheEntries, clearCache, getReason, getCachedResult, buildCacheKey, buildCacheKeySync, init };
|
export { classifyText, classifyTextSync, setConfig, removeCacheEntries, clearCache, getReason, getCachedResult, buildCacheKey, buildCacheKeySync, getCacheSize, init };
|
||||||
|
|
|
@ -180,6 +180,9 @@
|
||||||
<tr><th>Rule count</th><td id="rule-count"></td></tr>
|
<tr><th>Rule count</th><td id="rule-count"></td></tr>
|
||||||
<tr><th>Cache entries</th><td id="cache-count"></td></tr>
|
<tr><th>Cache entries</th><td id="cache-count"></td></tr>
|
||||||
<tr><th>Queue items</th><td id="queue-count"></td></tr>
|
<tr><th>Queue items</th><td id="queue-count"></td></tr>
|
||||||
|
<tr><th>Current run time</th><td id="current-time">--:--</td></tr>
|
||||||
|
<tr><th>Average run time</th><td id="average-time">--:--</td></tr>
|
||||||
|
<tr><th>Total run time</th><td id="total-time">--:--</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<button class="button is-danger" id="clear-cache" type="button">Clear Cache</button>
|
<button class="button is-danger" id="clear-cache" type="button">Clear Cache</button>
|
||||||
|
|
|
@ -305,20 +305,59 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
const ruleCountEl = document.getElementById('rule-count');
|
const ruleCountEl = document.getElementById('rule-count');
|
||||||
const cacheCountEl = document.getElementById('cache-count');
|
const cacheCountEl = document.getElementById('cache-count');
|
||||||
const queueCountEl = document.getElementById('queue-count');
|
const queueCountEl = document.getElementById('queue-count');
|
||||||
|
const currentTimeEl = document.getElementById('current-time');
|
||||||
|
const averageTimeEl = document.getElementById('average-time');
|
||||||
|
const totalTimeEl = document.getElementById('total-time');
|
||||||
|
let timingLogged = false;
|
||||||
ruleCountEl.textContent = (defaults.aiRules || []).length;
|
ruleCountEl.textContent = (defaults.aiRules || []).length;
|
||||||
cacheCountEl.textContent = defaults.aiCache ? Object.keys(defaults.aiCache).length : 0;
|
cacheCountEl.textContent = defaults.aiCache ? Object.keys(defaults.aiCache).length : 0;
|
||||||
|
|
||||||
async function refreshQueueCount() {
|
function format(ms) {
|
||||||
|
if (ms < 0) return '--:--';
|
||||||
|
return (ms / 1000).toFixed(1) + 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshMaintenance() {
|
||||||
try {
|
try {
|
||||||
const { count } = await browser.runtime.sendMessage({ type: 'sortana:getQueueCount' });
|
const stats = await browser.runtime.sendMessage({ type: 'sortana:getTiming' });
|
||||||
queueCountEl.textContent = count;
|
queueCountEl.textContent = stats.count;
|
||||||
|
currentTimeEl.classList.remove('has-text-success','has-text-danger');
|
||||||
|
let arrow = '';
|
||||||
|
if (stats.current >= 0) {
|
||||||
|
if (stats.stddev > 0 && stats.current - stats.average > stats.stddev) {
|
||||||
|
currentTimeEl.classList.add('has-text-danger');
|
||||||
|
arrow = ' ▲';
|
||||||
|
} else if (stats.stddev > 0 && stats.average - stats.current > stats.stddev) {
|
||||||
|
currentTimeEl.classList.add('has-text-success');
|
||||||
|
arrow = ' ▼';
|
||||||
|
}
|
||||||
|
currentTimeEl.textContent = format(stats.current) + arrow;
|
||||||
|
} else {
|
||||||
|
currentTimeEl.textContent = '--:--';
|
||||||
|
}
|
||||||
|
averageTimeEl.textContent = stats.count ? format(stats.average) : '--:--';
|
||||||
|
totalTimeEl.textContent = format(stats.total);
|
||||||
|
if (!timingLogged) {
|
||||||
|
logger.aiLog('retrieved timing stats', {debug: true});
|
||||||
|
timingLogged = true;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
queueCountEl.textContent = '?';
|
queueCountEl.textContent = '?';
|
||||||
|
currentTimeEl.textContent = '--:--';
|
||||||
|
averageTimeEl.textContent = '--:--';
|
||||||
|
totalTimeEl.textContent = '--:--';
|
||||||
|
}
|
||||||
|
|
||||||
|
ruleCountEl.textContent = document.querySelectorAll('#rules-container .rule').length;
|
||||||
|
try {
|
||||||
|
cacheCountEl.textContent = await AiClassifier.getCacheSize();
|
||||||
|
} catch {
|
||||||
|
cacheCountEl.textContent = '?';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshQueueCount();
|
refreshMaintenance();
|
||||||
setInterval(refreshQueueCount, 2000);
|
setInterval(refreshMaintenance, 2000);
|
||||||
|
|
||||||
document.getElementById('clear-cache').addEventListener('click', async () => {
|
document.getElementById('clear-cache').addEventListener('click', async () => {
|
||||||
await AiClassifier.clearCache();
|
await AiClassifier.clearCache();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue