refactoring and clean up

This commit is contained in:
Storme-bit
2026-04-07 01:30:35 -07:00
parent 0aea052311
commit 2b75f75733
18 changed files with 191 additions and 115 deletions

View File

@@ -1,12 +1,12 @@
const Database = require('better-sqlite3');
const schema = require('./schema');
const {getEnv } = require('@nexusai/shared');
const {getEnv, SQLITE } = require('@nexusai/shared');
let db; // Declare db variable in a scope accessible to all functions
function getDB() {
if (!db) {
const path = getEnv('SQLITE_PATH', './data/nexusai.db');
const path = getEnv('SQLITE_PATH', SQLITE.DEFAULT_PATH);
db = new Database(path);
db.pragma('journal_mode = WAL');

View File

@@ -1,4 +1,5 @@
const {getDB} = require('../db');
const { parseRow } = require ('@nexusai/shared')
/******* Entities ********/
@@ -21,13 +22,13 @@ function upsertEntity(name, type, notes = null, metadata = null) {
// Get an entity by its ID
function getEntity(id) {
const db = getDB();
return parseEntity(db.prepare(`SELECT * FROM entities WHERE id = ?`).get(id));
return parseRow(db.prepare(`SELECT * FROM entities WHERE id = ?`).get(id));
}
// Get all entities of a given type
function getEntitiesByType(type) {
const db = getDB();
return db.prepare(`SELECT * FROM entities WHERE type = ? ORDER BY name`).all(type).map(parseEntity);
return db.prepare(`SELECT * FROM entities WHERE type = ? ORDER BY name`).all(type).map(parseRow);
}
// Delete an entity by ID, cascades to delete relationships involving this entity
@@ -55,7 +56,7 @@ function upsertRelationship(fromId, toId, label, metadata = null){
function getRelationship(fromId, toId, label) {
const db = getDB();
return parseRelationship(
return parseRow(
db.prepare(`SELECT * FROM relationships WHERE from_id = ? AND to_id = ? AND label = ?`)
.get(fromId, toId, label)
);
@@ -64,13 +65,13 @@ function getRelationship(fromId, toId, label) {
// Retrieves an entity by its unique (name, type) combination
function getEntityByNameType(name, type) {
const db = getDB();
return parseEntity(db.prepare(`SELECT * FROM entities WHERE name = ? AND type = ?`).get(name, type));
return parseRow(db.prepare(`SELECT * FROM entities WHERE name = ? AND type = ?`).get(name, type));
}
// Retrive all relationships originating from a given entity
function getRelationshipsByEntity(entityId) {
const db = getDB();
return db.prepare(`SELECT * FROM relationships WHERE from_id = ?`).all(entityId).map(parseRelationship);
return db.prepare(`SELECT * FROM relationships WHERE from_id = ?`).all(entityId).map(parseRow);
}
// Delete a specific relationship by (from_id, to_id, label)
@@ -80,24 +81,6 @@ function deleteRelationship(fromId, toId, label) {
db.prepare(`DELETE FROM relationships WHERE from_id = ? AND to_id = ? AND label = ?`).run(fromId, toId, label);
}
/*********** Parse Functions ***********/
function parseEntity(row) {
if (!row) return null;
return {
...row,
metadata: row.metadata ? JSON.parse(row.metadata) : null
};
}
function parseRelationship(row) {
if (!row) return null;
return {
...row,
metadata: row.metadata ? JSON.parse(row.metadata) : null
};
}
module.exports = {
upsertEntity,
getEntity,

View File

@@ -1,5 +1,5 @@
const {getDB} = require('../db');
const { EPISODIC, getEnv, SERVICES } = require('@nexusai/shared');
const { EPISODIC, getEnv, SERVICES, parseRow, formatEpisodeText } = require('@nexusai/shared');
const semantic = require('../semantic');
// --Sessions --------------------------------------------------
@@ -20,7 +20,7 @@ function createSession(externalId, metadata = null) {
function getSession(id) {
const db = getDB();
const stmt = db.prepare(`SELECT * FROM sessions WHERE id = ?`);
return parseSession(stmt.get(id));
return parseRow(stmt.get(id));
}
function getSessions(limit = EPISODIC.DEFAULT_PAGE_SIZE, offset = 0) {
@@ -30,14 +30,14 @@ function getSessions(limit = EPISODIC.DEFAULT_PAGE_SIZE, offset = 0) {
ORDER BY updated_at DESC
LIMIT ? OFFSET ?
`);
return stmt.all(limit, offset).map(parseSession);
return stmt.all(limit, offset).map(parseRow);
}
// Retrieves a session by its external ID
function getSessionByExternalId(externalId) {
const db = getDB();
const stmt = db.prepare(`SELECT * FROM sessions WHERE external_id = ?`);
return parseSession(stmt.get(externalId));
return parseRow(stmt.get(externalId));
}
// Updates the updated_at timestamp of a session to the current time
@@ -103,7 +103,7 @@ function getEpisodesBySession(sessionId, limit = EPISODIC.DEFAULT_PAGE_SIZE, off
ORDER BY created_at DESC
LIMIT ? OFFSET ?
`);
return stmt.all(sessionId, limit, offset).map(parseEpisode);
return stmt.all(sessionId, limit, offset).map(parseRow);
}
// Retrieves recent episodes across all sessions, ordered by creation time descending, with a limit
@@ -115,7 +115,7 @@ function getRecentEpisodes(limit = EPISODIC.DEFAULT_RECENT_LIMIT) {
ORDER BY created_at DESC
LIMIT ?
`);
return stmt.all(limit).map(parseEpisode);
return stmt.all(limit).map(parseRow);
}
@@ -130,7 +130,7 @@ function searchEpisodes(query, limit = EPISODIC.DEFAULT_SEARCH_LIMIT) {
ORDER BY rank
LIMIT ?
`);
return stmt.all(query, limit).map(parseEpisode);
return stmt.all(query, limit).map(parseRow);
}
// Deletes an episode by its ID
@@ -139,32 +139,12 @@ function deleteEpisode(id) {
db.prepare(`DELETE FROM episodes WHERE id = ?`).run(id);
}
// ─── Parsers ──────────────────────────────────────────────────────────────────
// Parse JSON metadata back out on the way up — stored as string, returned as object
function parseSession(row) {
if (!row) return null;
return {
...row,
metadata: row.metadata ? JSON.parse(row.metadata) : null
};
}
// Parse JSON metadata back out on the way up — stored as string, returned as object
function parseEpisode(row) {
if (!row) return null;
return {
...row,
metadata: row.metadata ? JSON.parse(row.metadata) : null
};
}
/******** 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 text = formatEpisodeText(userMessage, aiResponse);
const res = await fetch(`${url}/embed`, {
method: 'POST',

View File

@@ -1,7 +1,8 @@
require ('dotenv').config();
const express = require('express');
const {getEnv} = require('@nexusai/shared');
const {getEnv, PORTS, EPISODIC} = require('@nexusai/shared');
const { getDB } = require('./db');
const episodic = require('./episodic');
const semantic = require('./semantic');
const entities = require('./entities');
@@ -9,7 +10,7 @@ const entities = require('./entities');
const app = express();
app.use(express.json());
const PORT = getEnv('PORT', '3002'); // Default to 3002 if PORT is not set
const PORT = getEnv('PORT', PORTS.MEMORY);
//initialize database on startup
const db = getDB();
@@ -42,7 +43,7 @@ app.post('/sessions', (req, res) => {
});
app.get('/sessions', (req, res) => {
const {limit = 20, offset = 0 } = req.query;
const {limit = EPISODIC.DEFAULT_PAGE_SIZE, offset = EPISODIC.DEFAULT_OFFSET } = req.query;
const sessions = episodic.getSessions(Number(limit), Number(offset));
res.json(sessions);
})
@@ -88,7 +89,7 @@ app.post('/episodes', async (req, res) => {
// Search MUST come before /:id — otherwise 'search' gets captured as an id
app.get('/episodes/search', (req, res) => {
const { q, limit = 10 } = req.query;
const { q, limit = EPISODIC.DEFAULT_PAGE_SIZE } = req.query;
if (!q) return res.status(400).json({ error: 'q (query) parameter is required' });
const results = episodic.searchEpisodes(q, Number(limit));
res.json(results);
@@ -102,7 +103,7 @@ app.get('/episodes/:id', (req, res) => {
// Get paginated episodes for a session
app.get('/sessions/:id/episodes', (req, res) => {
const { limit = 10, offset = 0 } = req.query;
const { limit = EPISODIC.DEFAULT_PAGE_SIZE, offset = EPISODIC.DEFAULT_OFFSET } = req.query;
const episodes = episodic.getEpisodesBySession(
req.params.id,
Number(limit),