From ed18022d1f1edcb6feb162afb293fc8321c47f05 Mon Sep 17 00:00:00 2001 From: Storme-bit Date: Sat, 4 Apr 2026 23:25:48 -0700 Subject: [PATCH] Wired embedding service into memory write path --- packages/memory-service/src/episodic/index.js | 37 +++++++++++++++++-- packages/shared/src/config/constants.js | 7 +++- packages/shared/src/index.js | 4 +- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/packages/memory-service/src/episodic/index.js b/packages/memory-service/src/episodic/index.js index d4a757b..df2d7e9 100644 --- a/packages/memory-service/src/episodic/index.js +++ b/packages/memory-service/src/episodic/index.js @@ -1,5 +1,6 @@ const {getDB} = require('../db'); -const { EPISODIC } = require('@nexusai/shared'); +const { EPISODIC, getEnv, SERVICES } = require('@nexusai/shared'); +const semantic = require('../semantic'); // --Sessions -------------------------------------------------- @@ -43,7 +44,7 @@ function deleteSession(id) { // --Episodes -------------------------------------------------- // Creates a new episode linked to a session, with user message, AI response, optional token count, and metadata -function createEpisode(sessionId, userMessage, aiResponse, tokenCount = null, metadata = null) { +async function createEpisode(sessionId, userMessage, aiResponse, tokenCount = null, metadata = null) { const db = getDB(); // Wrap insert + session touch in a transaction — both succeed or neither does @@ -63,7 +64,17 @@ function createEpisode(sessionId, userMessage, aiResponse, tokenCount = null, me return getEpisode(result.lastInsertRowid); }); - return insert(); + const episode= insert(); + + //embed ascynchronously after SQLite completes, non-blocking. If embedding fail, the episode still saved. + getEpisodeEmbedding(userMessage, aiResponse) + .then(vector => semantic.upsertEpisode(episode.id, vector, { + sessionId: episode.session_id, + createdAt: episode.created_at + })) + .catch(err => console.error(`Failed to embed episode ${episode.id}:`, err.message)); + + return episode; } // Retrieves an episode by its ID @@ -138,6 +149,26 @@ function parseEpisode(row) { }; } +/******** Embedding Helper ********/ +async function getEpisodeEmbedding(userMessage, aiResponse){ + const url = getEnv('EMBEDDING_SERVICE_URL', SERVICES.EMBEDDING_URL); + + //Combine user message and AI response for embedding + const text = `User: ${userMessage}\nAssistant: ${aiResponse}`; + + const res = await fetch(`${url}/embed`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ text }) + }) + + if (!res.ok) { + throw new Error(`Embedding service error: ${res.status} ${res.statusText}`); + } + const data = await res.json(); + return data.embedding; +} + module.exports = { createSession, getSession, diff --git a/packages/shared/src/config/constants.js b/packages/shared/src/config/constants.js index 657f6d0..b6bf149 100644 --- a/packages/shared/src/config/constants.js +++ b/packages/shared/src/config/constants.js @@ -19,8 +19,13 @@ const EPISODIC = { DEFAULT_SEARCH_LIMIT: 10, // Default number of search results to return }; +const SERVICES = { + EMBEDDING_URL: 'http://localhost:3003' +}; + module.exports = { QDRANT, COLLECTIONS, - EPISODIC + EPISODIC, + SERVICES }; \ No newline at end of file diff --git a/packages/shared/src/index.js b/packages/shared/src/index.js index fcda50a..b8c5510 100644 --- a/packages/shared/src/index.js +++ b/packages/shared/src/index.js @@ -1,4 +1,4 @@ const {getEnv} = require('./config/env'); -const {QDRANT, COLLECTIONS, EPISODIC } = require('./config/constants'); +const {QDRANT, COLLECTIONS, EPISODIC, SERVICES } = require('./config/constants'); -module.exports = {getEnv, QDRANT, COLLECTIONS, EPISODIC}; \ No newline at end of file +module.exports = {getEnv, QDRANT, COLLECTIONS, EPISODIC, SERVICES}; \ No newline at end of file