feat(memory): add automated nightly consolidation at 4:00 AM UTC

Step 2 of memory system overhaul: automated scheduling.

- New consolidation_scheduler.py: run_nightly_consolidation() function that
  checks Cat health, triggers consolidation via WebSocket, and tracks
  run history with success/failure stats
- bot.py on_ready: register APScheduler cron job (hour=4, minute=0)
  alongside the existing daily DM analysis job
- routes/memory.py: expose consolidation status (last_run, last_result,
  last_error, is_running, total_runs, successful_runs) in the
  /memory/status API response
- Web UI: show consolidation schedule info (last run time, success/fail,
  run counts) below the manual consolidate button, with 'running now'
  indicator when active

The 'sleep consolidation' metaphor is now actually automated instead of
being manual-only.
This commit is contained in:
2026-05-15 13:54:54 +03:00
parent 811bcc0a5d
commit f3c4a8fe5a
5 changed files with 146 additions and 2 deletions

View File

@@ -1034,6 +1034,7 @@
<h4 style="margin: 0 0 0.5rem 0;">🌙 Memory Consolidation</h4>
<p style="color: #aaa; font-size: 0.85rem; margin-bottom: 0.75rem;">
Trigger the sleep consolidation process: analyzes episodic memories, extracts important facts, and removes trivial entries.
<br><span style="color: #888;">⏰ Scheduled nightly at 4:00 AM UTC</span>
</p>
<div style="display: flex; gap: 0.5rem; align-items: center;">
<button id="consolidate-btn" onclick="triggerConsolidation()" style="background: #5b3a8c; color: #fff; padding: 0.5rem 1rem; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
@@ -1041,6 +1042,7 @@
</button>
<span id="consolidation-status" style="color: #888; font-size: 0.85rem;"></span>
</div>
<div id="consolidation-schedule-info" style="margin-top: 0.5rem; color: #666; font-size: 0.78rem;"></div>
<div id="consolidation-result" style="display: none; margin-top: 0.75rem; background: #111; border: 1px solid #333; border-radius: 4px; padding: 0.75rem; font-size: 0.85rem; color: #ccc; white-space: pre-wrap; max-height: 200px; overflow-y: auto;"></div>
</div>

View File

@@ -39,6 +39,30 @@ async function refreshMemoryStats() {
document.getElementById('stat-declarative-count').textContent = '—';
document.getElementById('stat-procedural-count').textContent = '—';
}
// Show consolidation schedule info if available
const consInfo = document.getElementById('consolidation-schedule-info');
if (consInfo && statusData.consolidation) {
let infoHtml = '';
const cons = statusData.consolidation;
if (cons.last_run) {
const lastRun = new Date(cons.last_run).toLocaleString();
infoHtml += `🕐 Last run: ${lastRun}`;
if (cons.last_error) {
infoHtml += ` <span style="color: #ff6b6b;">❌ ${escapeHtml(cons.last_error)}</span>`;
} else if (cons.last_result) {
infoHtml += ` <span style="color: #6fdc6f;">✅</span>`;
}
infoHtml += `<br>`;
} else {
infoHtml += `🕐 Last run: never<br>`;
}
infoHtml += `📊 Runs: ${cons.successful_runs}/${cons.total_runs} successful`;
if (cons.is_running) {
infoHtml += ` <span style="color: #dcb06f;">⏳ (running now)</span>`;
}
consInfo.innerHTML = infoHtml;
}
} catch (err) {
console.error('Error refreshing memory stats:', err);
document.getElementById('cat-status-indicator').innerHTML = '<span style="color: #ff6b6b;">● Error checking status</span>';