diff --git a/packages/memory-service/src/episodic/index.js b/packages/memory-service/src/episodic/index.js index 64c03cd..187413c 100644 --- a/packages/memory-service/src/episodic/index.js +++ b/packages/memory-service/src/episodic/index.js @@ -266,6 +266,19 @@ function deleteEpisode(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 ********/ async function getEpisodeEmbedding(userMessage, aiResponse){ const url = getEnv('EMBEDDING_SERVICE_URL', SERVICES.EMBEDDING_URL); @@ -315,6 +328,7 @@ module.exports = { getEpisodesSince, searchEpisodes, deleteEpisode, + touchEpisodes, getEpisodesByProject, buildFtsQuery, }; \ No newline at end of file diff --git a/packages/memory-service/src/index.js b/packages/memory-service/src/index.js index fc7263a..af2e644 100644 --- a/packages/memory-service/src/index.js +++ b/packages/memory-service/src/index.js @@ -139,6 +139,13 @@ app.get('/episodes/search', (req, res) => { 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) => { const episode = episodic.getEpisode(req.params.id); if (!episode) return res.status(404).json({ error: 'Episode not found' }); diff --git a/packages/orchestration-service/src/chat/index.js b/packages/orchestration-service/src/chat/index.js index c655bc7..ada8be2 100644 --- a/packages/orchestration-service/src/chat/index.js +++ b/packages/orchestration-service/src/chat/index.js @@ -331,6 +331,9 @@ async function assembleContext(externalId, userMessage) { const scoredPool = buildScoredPool(fusedWithScores, recentEpisodes, entityBoostedIds, { entityWeight }); const { guaranteed, selected } = selectWithinBudget(scoredPool, contextBudget, minRecentEpisodes, recentEpisodes); + const selectedIds = selected.map(ep => ep.id); + memory.touchEpisodes(selectedIds); + // 7. Graph neighborhood expansion let neighborhood = { nodes: [], edges: [] }; if (entityIds.length > 0) { diff --git a/packages/orchestration-service/src/services/memory.js b/packages/orchestration-service/src/services/memory.js index 4045605..a87bba5 100644 --- a/packages/orchestration-service/src/services/memory.js +++ b/packages/orchestration-service/src/services/memory.js @@ -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); @@ -223,6 +223,16 @@ async function getEntityIdsBySession(sessionId){ 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 = { getSessionByExternalId, createSession, @@ -250,4 +260,5 @@ module.exports = { searchEpisodes, getEpisodesByEntities, getEntityIdsBySession, + touchEpisodes, } \ No newline at end of file diff --git a/test/migrations.test.js b/test/migrations.test.js index 4220584..e537982 100644 --- a/test/migrations.test.js +++ b/test/migrations.test.js @@ -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)', () => { const db = fakeDb(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.deepStrictEqual(db.setCalls, [], 'should not touch user_version when current'); });