Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd8867ee96 | ||
|
|
00101ba14a |
@@ -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,
|
||||
};
|
||||
@@ -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' });
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
// advance user_version correctly, and no-op when the DB is already current.
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const Database = require('better-sqlite3');
|
||||
const { migrate, LATEST_VERSION } = require('../packages/memory-service/src/db/migrations');
|
||||
|
||||
// Emulates just the slice of the better-sqlite3 API that migrate() uses.
|
||||
@@ -26,14 +25,16 @@ function fakeDb(startVersion = 0) {
|
||||
|
||||
test('a fresh DB (user_version 0) is stamped to LATEST_VERSION', () => {
|
||||
const db = fakeDb(0);
|
||||
const result = migrate(db);
|
||||
const stubs = Array.from({ length: LATEST_VERSION }, () => () => {});
|
||||
const result = migrate(db, stubs); // inject no-op migrations
|
||||
assert.strictEqual(result, LATEST_VERSION);
|
||||
assert.strictEqual(db.version, LATEST_VERSION);
|
||||
});
|
||||
|
||||
test('an already-current DB is a no-op (no version writes)', () => {
|
||||
const db = fakeDb(LATEST_VERSION);
|
||||
const result = migrate(db);
|
||||
const stubs = Array.from({ length: LATEST_VERSION }, () => () => {});
|
||||
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');
|
||||
});
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ try { ({ DatabaseSync } = require('node:sqlite')); } catch { DatabaseSync = null
|
||||
// The complete intended shape = original base tables + every historical ALTER.
|
||||
const EXPECTED = {
|
||||
sessions: ['id','external_id','created_at','updated_at','metadata','name','project_id'],
|
||||
episodes: ['id','session_id','user_message','ai_response','created_at','token_count','metadata'],
|
||||
episodes: ['access_count', 'ai_response', 'created_at', 'id', 'last_accessed_at', 'metadata', 'session_id', 'token_count', 'user_message'],
|
||||
entities: ['id','name','type','notes','created_at','updated_at','metadata','mention_count','confidence','source','last_seen_at'],
|
||||
relationships: ['id','from_id','to_id','label','created_at','metadata','mention_count','notes'],
|
||||
entity_episodes: ['entity_id','episode_id'],
|
||||
|
||||
@@ -19,7 +19,7 @@ function mockMemory({ stats, summaries, since }) {
|
||||
calls.sinceAfterId = Number(u.split('/since/').at(-1));
|
||||
return json(since.filter(ep => ep.id > calls.sinceAfterId));
|
||||
}
|
||||
if (u.includes('/api/generate')) return json({ response: 'A concise third-person summary.' });
|
||||
if (u.endsWith('/utility/complete')) return json({ text: 'A concise third-person summary.' });
|
||||
if (u.endsWith('/summaries') && opts.method === 'POST') { calls.posted = JSON.parse(opts.body); return json({ id: 99 }); }
|
||||
if (/\/summaries\/\d+$/.test(u) && opts.method === 'PATCH') { calls.patched = JSON.parse(opts.body); return json({ ok: true }); }
|
||||
throw new Error('unexpected fetch: ' + u);
|
||||
|
||||
Reference in New Issue
Block a user