perf(client): reduce redundant summary and polling API calls (#2565)

* perf(client): reduce redundant summary and polling API calls

- summary cache capacity 32→128 to eliminate eviction-driven re-fetches
- IntelligenceGapBadge: skip polling when tab not visible
- McpConnectModal: raise min refresh interval 10→60s

* fix(tests): replace eval(), harden regexes, extract MCP refresh constant

- Replace eval() in summary-cache-capacity test with regex assertion
- Scope persistCache test to summaryResultBreaker block
- Harden intelligence-gap-badge regex: [^}]* → [\s\S]*? (nested braces)
- Extract MIN_MCP_REFRESH_S constant in McpConnectModal (DRY)
- Update mcp-connect-modal test to verify constant usage

---------

Co-authored-by: Elie Habib <elie.habib@gmail.com>
This commit is contained in:
John Pham
2026-03-31 01:21:11 -07:00
committed by GitHub
parent b72ec18e65
commit c68906ddb0
6 changed files with 77 additions and 5 deletions

View File

@@ -229,7 +229,9 @@ export class IntelligenceFindingsBadge {
private startRefresh(): void {
document.addEventListener('wm:intelligence-updated', this.boundUpdate);
this.refreshInterval = setInterval(this.boundUpdate, REFRESH_INTERVAL_MS);
this.refreshInterval = setInterval(() => {
if (document.visibilityState === 'visible') this.boundUpdate();
}, REFRESH_INTERVAL_MS);
}
public update(): void {

View File

@@ -10,6 +10,8 @@ interface McpConnectOptions {
onComplete: (spec: McpPanelSpec) => void;
}
const MIN_MCP_REFRESH_S = 60;
let overlay: HTMLElement | null = null;
/** Build a header Record from a template + key value.
@@ -141,8 +143,8 @@ export function openMcpConnectModal(options: McpConnectOptions): void {
</div>
<div class="mcp-form-group mcp-refresh-group">
<label class="mcp-label">${escapeHtml(t('mcp.refreshEvery'))}</label>
<input class="mcp-input mcp-refresh-input" type="number" min="10" max="86400"
value="${existing ? Math.round(existing.refreshIntervalMs / 1000) : 60}" />
<input class="mcp-input mcp-refresh-input" type="number" min="${MIN_MCP_REFRESH_S}" max="86400"
value="${existing ? Math.round(existing.refreshIntervalMs / 1000) : MIN_MCP_REFRESH_S}" />
<span class="mcp-refresh-unit">${escapeHtml(t('mcp.seconds'))}</span>
</div>
</div>
@@ -428,7 +430,7 @@ export function openMcpConnectModal(options: McpConnectOptions): void {
customHeaders: getEffectiveHeaders(),
toolName: selectedTool.name,
toolArgs,
refreshIntervalMs: Math.max(10, parseInt(refreshInput.value, 10) || 60) * 1000,
refreshIntervalMs: Math.max(MIN_MCP_REFRESH_S, parseInt(refreshInput.value, 10) || MIN_MCP_REFRESH_S) * 1000,
createdAt: existing?.createdAt ?? Date.now(),
updatedAt: Date.now(),
};

View File

@@ -43,7 +43,7 @@ const summaryResultBreaker = createCircuitBreaker<SummarizationResult | null>({
name: 'SummaryResult',
cacheTtlMs: 2 * 60 * 60 * 1000,
persistCache: true,
maxCacheEntries: 32,
maxCacheEntries: 128,
});
const emptySummaryFallback: SummarizeArticleResponse = { summary: '', provider: '', model: '', fallback: true, tokens: 0, error: '', errorType: '', status: 'SUMMARIZE_STATUS_UNSPECIFIED', statusDetail: '' };

View File

@@ -0,0 +1,18 @@
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const src = readFileSync(resolve(__dirname, '..', 'src', 'components', 'IntelligenceGapBadge.ts'), 'utf-8');
describe('IntelligenceGapBadge polling', () => {
it('setInterval callback includes visibilityState check', () => {
assert.match(
src,
/setInterval\(\s*\(\)\s*=>\s*\{[\s\S]*?visibilityState/s,
'setInterval callback must check document.visibilityState to avoid background-tab polling',
);
});
});

View File

@@ -0,0 +1,24 @@
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const src = readFileSync(resolve(__dirname, '..', 'src', 'components', 'McpConnectModal.ts'), 'utf-8');
describe('McpConnectModal refresh interval', () => {
it('MIN_MCP_REFRESH_S constant is at least 60', () => {
const m = src.match(/const\s+MIN_MCP_REFRESH_S\s*=\s*(\d+)/);
assert.ok(m, 'MIN_MCP_REFRESH_S constant not found');
assert.ok(Number(m![1]) >= 60, `MIN_MCP_REFRESH_S is ${m![1]}, expected >= 60`);
});
it('Math.max uses MIN_MCP_REFRESH_S constant', () => {
assert.match(src, /Math\.max\(MIN_MCP_REFRESH_S,\s*parseInt\(refreshInput/, 'Math.max should use MIN_MCP_REFRESH_S');
});
it('HTML input min uses MIN_MCP_REFRESH_S constant', () => {
assert.match(src, /min="\$\{MIN_MCP_REFRESH_S\}"/, 'HTML min should use MIN_MCP_REFRESH_S');
});
});

View File

@@ -0,0 +1,26 @@
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const src = readFileSync(resolve(__dirname, '..', 'src', 'services', 'summarization.ts'), 'utf-8');
describe('summary circuit breaker configuration', () => {
it('maxCacheEntries is at least 128', () => {
const m = src.match(/maxCacheEntries:\s*(\d+)/);
assert.ok(m, 'maxCacheEntries not found in summarization.ts');
assert.ok(Number(m![1]) >= 128, `maxCacheEntries is ${m![1]}, expected >= 128`);
});
it('persistCache is enabled on summaryResultBreaker', () => {
assert.match(src, /summaryResultBreaker[\s\S]*?persistCache:\s*true/, 'persistCache should be true on summaryResultBreaker');
});
it('cacheTtlMs is 2 hours on summaryResultBreaker', () => {
const block = src.match(/summaryResultBreaker[\s\S]*?cacheTtlMs:\s*([\d\s*]+)/);
assert.ok(block, 'cacheTtlMs not found in summaryResultBreaker block');
assert.match(block![1].trim(), /^2\s*\*\s*60\s*\*\s*60\s*\*\s*1000$/, `cacheTtlMs should be 2 * 60 * 60 * 1000, got "${block![1].trim()}"`);
});
});