consolidation and aging
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
const {getDB} = require('../db');
|
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 semantic = require('../semantic');
|
||||||
const { extractAndStoreEntities } = require('../entities/extraction')
|
const { extractAndStoreEntities } = require('../entities/extraction')
|
||||||
|
|
||||||
@@ -311,6 +311,22 @@ function getEpisodesByProject(projectId, limit = SUMMARIES.MAX_PROJECT_EPISODE_L
|
|||||||
`).all(projectId, limit).map(parseRow);
|
`).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 = {
|
module.exports = {
|
||||||
createSession,
|
createSession,
|
||||||
getSession,
|
getSession,
|
||||||
@@ -331,4 +347,5 @@ module.exports = {
|
|||||||
touchEpisodes,
|
touchEpisodes,
|
||||||
getEpisodesByProject,
|
getEpisodesByProject,
|
||||||
buildFtsQuery,
|
buildFtsQuery,
|
||||||
|
getConsolidationCandidates,
|
||||||
};
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
require ('dotenv').config();
|
require ('dotenv').config();
|
||||||
const express = require('express');
|
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 { getDB } = require('./db');
|
||||||
const { createProject, getProjects, getProject, updateProject, deleteProject } = require('./db/projects');
|
const { createProject, getProjects, getProject, updateProject, deleteProject } = require('./db/projects');
|
||||||
const { createSummary, getSummary, getSummariesBySession, getSummariesByProject, updateSummary, deleteSummary } = require('./db/summaries');
|
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);
|
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) => {
|
app.delete('/episodes/:id', (req, res) => {
|
||||||
const id = Number(req.params.id);
|
const id = Number(req.params.id);
|
||||||
episodic.deleteEpisode(id);
|
episodic.deleteEpisode(id);
|
||||||
|
|||||||
@@ -120,6 +120,12 @@ const UTILITY = {
|
|||||||
TIMEOUT_MS: 120_000, // extraction previously used 60s; summaries had none — standardized
|
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 = {
|
module.exports = {
|
||||||
QDRANT,
|
QDRANT,
|
||||||
COLLECTIONS,
|
COLLECTIONS,
|
||||||
@@ -135,4 +141,5 @@ module.exports = {
|
|||||||
ENTITIES,
|
ENTITIES,
|
||||||
RETRIEVAL,
|
RETRIEVAL,
|
||||||
UTILITY,
|
UTILITY,
|
||||||
|
CONSOLIDATION,
|
||||||
};
|
};
|
||||||
@@ -13,7 +13,8 @@ const {
|
|||||||
SUMMARIES,
|
SUMMARIES,
|
||||||
ENTITIES,
|
ENTITIES,
|
||||||
RETRIEVAL,
|
RETRIEVAL,
|
||||||
UTILITY
|
UTILITY,
|
||||||
|
CONSOLIDATION
|
||||||
} = require('./config/constants');
|
} = require('./config/constants');
|
||||||
const {parseRow, formatEpisodeText, isTrivialTurn} = require('./utils')
|
const {parseRow, formatEpisodeText, isTrivialTurn} = require('./utils')
|
||||||
|
|
||||||
@@ -40,5 +41,6 @@ module.exports = {
|
|||||||
logger,
|
logger,
|
||||||
RETRIEVAL,
|
RETRIEVAL,
|
||||||
UTILITY,
|
UTILITY,
|
||||||
|
CONSOLIDATION,
|
||||||
utilityInference,
|
utilityInference,
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user