episodes touch

This commit is contained in:
Storme-bit
2026-08-23 19:24:10 -07:00
parent 00101ba14a
commit fd8867ee96
5 changed files with 37 additions and 2 deletions
@@ -266,6 +266,19 @@ function deleteEpisode(id) {
db.prepare(`DELETE FROM episodes WHERE id = ?`).run(id); db.prepare(`DELETE FROM episodes WHERE id = ?`).run(id);
} }
function touchEpisodes(ids) {
if (!ids.length) return;
const db = getDB(); // <-- missing in your version
const placeholders = ids.map(() => '?').join(',');
db.prepare(`
UPDATE episodes
SET access_count = access_count + 1,
last_accessed_at = ?
WHERE id IN (${placeholders})
`).run(Date.now(), ...ids);
}
/******** Embedding Helper ********/ /******** Embedding Helper ********/
async function getEpisodeEmbedding(userMessage, aiResponse){ async function getEpisodeEmbedding(userMessage, aiResponse){
const url = getEnv('EMBEDDING_SERVICE_URL', SERVICES.EMBEDDING_URL); const url = getEnv('EMBEDDING_SERVICE_URL', SERVICES.EMBEDDING_URL);
@@ -315,6 +328,7 @@ module.exports = {
getEpisodesSince, getEpisodesSince,
searchEpisodes, searchEpisodes,
deleteEpisode, deleteEpisode,
touchEpisodes,
getEpisodesByProject, getEpisodesByProject,
buildFtsQuery, buildFtsQuery,
}; };
+7
View File
@@ -139,6 +139,13 @@ app.get('/episodes/search', (req, res) => {
res.json(episodic.searchEpisodes(q, Number(limit), parsedSessionIds)); res.json(episodic.searchEpisodes(q, Number(limit), parsedSessionIds));
}); });
app.post('/episodes/touch', (req, res) => {
const { ids } = req.body;
if(!Array.isArray(ids)) return res.status(400).json({error: 'ids must be an array'});
episodic.touchEpisodes(ids);
res.json({touched: ids.length});
})
app.get('/episodes/:id', (req, res) => { app.get('/episodes/:id', (req, res) => {
const episode = episodic.getEpisode(req.params.id); const episode = episodic.getEpisode(req.params.id);
if (!episode) return res.status(404).json({ error: 'Episode not found' }); if (!episode) return res.status(404).json({ error: 'Episode not found' });
@@ -331,6 +331,9 @@ async function assembleContext(externalId, userMessage) {
const scoredPool = buildScoredPool(fusedWithScores, recentEpisodes, entityBoostedIds, { entityWeight }); const scoredPool = buildScoredPool(fusedWithScores, recentEpisodes, entityBoostedIds, { entityWeight });
const { guaranteed, selected } = selectWithinBudget(scoredPool, contextBudget, minRecentEpisodes, recentEpisodes); const { guaranteed, selected } = selectWithinBudget(scoredPool, contextBudget, minRecentEpisodes, recentEpisodes);
const selectedIds = selected.map(ep => ep.id);
memory.touchEpisodes(selectedIds);
// 7. Graph neighborhood expansion // 7. Graph neighborhood expansion
let neighborhood = { nodes: [], edges: [] }; let neighborhood = { nodes: [], edges: [] };
if (entityIds.length > 0) { if (entityIds.length > 0) {
@@ -1,4 +1,4 @@
const { getEnv, SERVICES, EPISODIC } = require('@nexusai/shared'); const { getEnv, SERVICES, EPISODIC, logger } = require('@nexusai/shared');
const BASE_URL = getEnv('MEMORY_SERVICE_URL', SERVICES.MEMORY_URL); const BASE_URL = getEnv('MEMORY_SERVICE_URL', SERVICES.MEMORY_URL);
@@ -223,6 +223,16 @@ async function getEntityIdsBySession(sessionId){
return entityIds; return entityIds;
} }
// orchestration-service/src/services/memory.js
async function touchEpisodes(ids) {
if (!ids.length) return;
fetch(`${BASE_URL}/episodes/touch`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ids }),
}).catch(err => logger.warn(`[memory] touch failed: ${err.message}`));
}
module.exports = { module.exports = {
getSessionByExternalId, getSessionByExternalId,
createSession, createSession,
@@ -250,4 +260,5 @@ module.exports = {
searchEpisodes, searchEpisodes,
getEpisodesByEntities, getEpisodesByEntities,
getEntityIdsBySession, getEntityIdsBySession,
touchEpisodes,
} }
+1 -1
View File
@@ -34,7 +34,7 @@ test('a fresh DB (user_version 0) is stamped to LATEST_VERSION', () => {
test('an already-current DB is a no-op (no version writes)', () => { test('an already-current DB is a no-op (no version writes)', () => {
const db = fakeDb(LATEST_VERSION); const db = fakeDb(LATEST_VERSION);
const stubs = Array.from({ length: LATEST_VERSION }, () => () => {}); const stubs = Array.from({ length: LATEST_VERSION }, () => () => {});
const result = migrate(stubs); const result = migrate(db, stubs); // fixed: db is now the first arg
assert.strictEqual(result, LATEST_VERSION); assert.strictEqual(result, LATEST_VERSION);
assert.deepStrictEqual(db.setCalls, [], 'should not touch user_version when current'); assert.deepStrictEqual(db.setCalls, [], 'should not touch user_version when current');
}); });