Wired embedding service into memory write path

This commit is contained in:
Storme-bit
2026-04-04 23:25:48 -07:00
parent 770d123419
commit ed18022d1f
3 changed files with 42 additions and 6 deletions

View File

@@ -1,5 +1,6 @@
const {getDB} = require('../db'); const {getDB} = require('../db');
const { EPISODIC } = require('@nexusai/shared'); const { EPISODIC, getEnv, SERVICES } = require('@nexusai/shared');
const semantic = require('../semantic');
// --Sessions -------------------------------------------------- // --Sessions --------------------------------------------------
@@ -43,7 +44,7 @@ function deleteSession(id) {
// --Episodes -------------------------------------------------- // --Episodes --------------------------------------------------
// Creates a new episode linked to a session, with user message, AI response, optional token count, and metadata // 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(); const db = getDB();
// Wrap insert + session touch in a transaction — both succeed or neither does // 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 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 // 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 = { module.exports = {
createSession, createSession,
getSession, getSession,

View File

@@ -19,8 +19,13 @@ const EPISODIC = {
DEFAULT_SEARCH_LIMIT: 10, // Default number of search results to return DEFAULT_SEARCH_LIMIT: 10, // Default number of search results to return
}; };
const SERVICES = {
EMBEDDING_URL: 'http://localhost:3003'
};
module.exports = { module.exports = {
QDRANT, QDRANT,
COLLECTIONS, COLLECTIONS,
EPISODIC EPISODIC,
SERVICES
}; };

View File

@@ -1,4 +1,4 @@
const {getEnv} = require('./config/env'); 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}; module.exports = {getEnv, QDRANT, COLLECTIONS, EPISODIC, SERVICES};