consolidation and aging

This commit is contained in:
Storme-bit
2026-08-23 22:41:21 -07:00
parent fd8867ee96
commit 4b2099498c
4 changed files with 49 additions and 3 deletions
+18 -1
View File
@@ -1,5 +1,5 @@
const {getDB} = require('../db');
const { EPISODIC, getEnv, SERVICES, parseRow, formatEpisodeText, SUMMARIES, logger, isTrivialTurn } = require('@nexusai/shared');
const { EPISODIC, getEnv, SERVICES, parseRow, formatEpisodeText, SUMMARIES, logger, isTrivialTurn, CONSOLIDATION } = require('@nexusai/shared');
const semantic = require('../semantic');
const { extractAndStoreEntities } = require('../entities/extraction')
@@ -311,6 +311,22 @@ function getEpisodesByProject(projectId, limit = SUMMARIES.MAX_PROJECT_EPISODE_L
`).all(projectId, limit).map(parseRow);
}
function getConsolidationCandidates(sessionId, limit = CONSOLIDATION.CANDIDATE_LIMIT) {
const db = getDB();
const now = Date.now();
const cutoff = Math.floor(now / 1000) - CONSOLIDATION.MIN_AGE_DAYS *86400 // seconds, so it matches created_at
return db.prepare(`
SELECT id, access_count, created_at, last_accessed_at,
SUBSTR(user_message, 1, 80) AS preview,
CAST( access_count AS REAL) / (1+(?-COALESCE(last_accessed_at, created_at * 1000)) / 86400000.0) AS aging_score
FROM episodes
WHERE session_id = ? AND created_at < ?
ORDER BY aging_score ASC
LIMIT ?
`).all(now, sessionId, cutoff, limit);
}
module.exports = {
createSession,
getSession,
@@ -331,4 +347,5 @@ module.exports = {
touchEpisodes,
getEpisodesByProject,
buildFtsQuery,
getConsolidationCandidates,
};
+21 -1
View File
@@ -1,6 +1,6 @@
require ('dotenv').config();
const express = require('express');
const {getEnv, PORTS, EPISODIC, logger} = require('@nexusai/shared');
const {getEnv, PORTS, EPISODIC, logger, CONSOLIDATION} = require('@nexusai/shared');
const { getDB } = require('./db');
const { createProject, getProjects, getProject, updateProject, deleteProject } = require('./db/projects');
const { createSummary, getSummary, getSummariesBySession, getSummariesByProject, updateSummary, deleteSummary } = require('./db/summaries');
@@ -182,6 +182,26 @@ app.get('/sessions/:id/episodes/since/:afterId', (req, res) => {
res.json(episodes);
});
app.get('/sessions/:id/consolidation-candidates', (req, res) => {
const sessionId = Number(req.params.id);
const stats = episodic.getSessionEpisodeStats(sessionId);
if(stats.count < CONSOLIDATION.MIN_SESSION_EPISODES) {
return res.json({
eligible: false,
reason: `session has ${stats.count} episodes, floor is ${CONSOLIDATION.MIN_SESSION_EPISODES}`,
candidates: [],
});
}
const candidates = episodic.getConsolidationCandidates(sessionId);
res.json({
eligible:true,
sessionEpisodeCount: stats.count,
candidates
})
})
app.delete('/episodes/:id', (req, res) => {
const id = Number(req.params.id);
episodic.deleteEpisode(id);
+7
View File
@@ -120,6 +120,12 @@ const UTILITY = {
TIMEOUT_MS: 120_000, // extraction previously used 60s; summaries had none — standardized
};
const CONSOLIDATION = {
MIN_AGE_DAYS: 7, // episodes younger than this are never consolidation candidates
MIN_SESSION_EPISODES: 20, // sessions smaller than this are left entirely alone
CANDIDATE_LIMIT: 50,
}
module.exports = {
QDRANT,
COLLECTIONS,
@@ -135,4 +141,5 @@ module.exports = {
ENTITIES,
RETRIEVAL,
UTILITY,
CONSOLIDATION,
};
+3 -1
View File
@@ -13,7 +13,8 @@ const {
SUMMARIES,
ENTITIES,
RETRIEVAL,
UTILITY
UTILITY,
CONSOLIDATION
} = require('./config/constants');
const {parseRow, formatEpisodeText, isTrivialTurn} = require('./utils')
@@ -40,5 +41,6 @@ module.exports = {
logger,
RETRIEVAL,
UTILITY,
CONSOLIDATION,
utilityInference,
};