diff --git a/packages/embedding-service/src/index.js b/packages/embedding-service/src/index.js index bf65dd7..406b6ce 100644 --- a/packages/embedding-service/src/index.js +++ b/packages/embedding-service/src/index.js @@ -1,16 +1,13 @@ require ('dotenv').config(); const express = require('express'); -const {getEnv} = require('@nexusai/shared'); +const {getEnv, OLLAMA, PORTS} = require('@nexusai/shared'); const app = express(); app.use(express.json()); -const PORT = getEnv('PORT', '3003'); // Default to 3003 if PORT is not set -const OLLAMA_URL = getEnv('OLLAMA_URL', 'http://localhost:11434'); // URL for Ollama API -const EMBED_MODEL = getEnv('EMBEDDING_MODEL', 'nomic-embed-text'); // Ollama model for embeddings - -console.log('OLLAMA_URL:', OLLAMA_URL); -console.log('EMBED_MODEL:', EMBED_MODEL); +const PORT = getEnv('PORT', PORTS.EMBEDDING); // Default to 3003 if PORT is not set +const OLLAMA_URL = getEnv('OLLAMA_URL', OLLAMA.DEFAULT_URL); // URL for Ollama API +const EMBED_MODEL = getEnv('EMBEDDING_MODEL', OLLAMA.EMBED_MODEL); // Ollama model for embeddings //OLLAMA embedding helper function async function embedText(text) { diff --git a/packages/inference-service/src/index.js b/packages/inference-service/src/index.js index fe222f7..b143ce0 100644 --- a/packages/inference-service/src/index.js +++ b/packages/inference-service/src/index.js @@ -1,20 +1,22 @@ require ('dotenv').config(); const express = require('express'); -const {getEnv} = require('@nexusai/shared'); +const {getEnv, PORTS, OLLAMA} = require('@nexusai/shared'); const inferenceRouter = require('./routes/inference'); const app = express(); app.use(express.json()); -const PORT = getEnv('PORT', '3001'); // Default to 3001 if PORT is not set +const PORT = getEnv('PORT', PORTS.INFERENCE); +const PROVIDER = getEnv('INFERENCE_PROVIDER', 'ollama'); +const MODEL = getEnv('DEFAULT_MODEL', OLLAMA.OLLAMA_MODEL) // Health check endpoint app.get('/health', (req, res) => { res.json({ service: 'Inference Service', status: 'healthy', - provider: getEnv('INFERENCE_PROVIDER', 'ollama'), - model: getEnv('DEFAULT_MODEL', 'llama3.2') + provider: PROVIDER, + model: MODEL }); }); diff --git a/packages/inference-service/src/providers/llamacpp.js b/packages/inference-service/src/providers/llamacpp.js index ef5e32b..5c5e8b2 100644 --- a/packages/inference-service/src/providers/llamacpp.js +++ b/packages/inference-service/src/providers/llamacpp.js @@ -1,15 +1,32 @@ -const { getEnv } = require('@nexusai/shared'); +const { getEnv, LLAMACPP, INFERENCE_DEFAULTS } = require('@nexusai/shared'); -const BASE_URL = getEnv('INFERENCE_URL', 'http://localhost:8080'); -const DEFAULT_MODEL = getEnv('DEFAULT_MODEL', 'local-model'); +const BASE_URL = getEnv('INFERENCE_URL', LLAMACPP.DEFAULT_URL); +const DEFAULT_MODEL = getEnv('DEFAULT_MODEL', LLAMACPP.DEFAULT_MODEL); + +function resolveOptions(options) { + return { + temperature: options.temperature ?? INFERENCE_DEFAULTS.TEMPERATURE, + maxTokens: options.maxTokens ?? INFERENCE_DEFAULTS.MAX_TOKENS, + topP: options.topP ?? INFERENCE_DEFAULTS.TOP_P, + topK: options.topK ?? INFERENCE_DEFAULTS.TOP_K, + repeatPenalty: options.repeatPenalty ?? INFERENCE_DEFAULTS.REPEAT_PENALTY, + seed: options.seed ?? INFERENCE_DEFAULTS.SEED, + }; +} function buildPayload(prompt, options, stream = false){ + const opts = resolveOptions(options); + return { model: options.model || DEFAULT_MODEL, messages: [{ role: 'user', content: prompt }], - temperature: options.temperature ?? 0.7, - max_tokens: options.num_predict ?? 1024, + temperature: opts.temperature, + max_tokens: opts.maxTokens, + top_p: opts.topP, + top_k: opts.topK, + repeat_penalty: opts.repeatPenalty, stream, + ...(opts.seed !== null && { seed: opts.seed }), }; } diff --git a/packages/inference-service/src/providers/ollama.js b/packages/inference-service/src/providers/ollama.js index bc3f42f..7bda6f2 100644 --- a/packages/inference-service/src/providers/ollama.js +++ b/packages/inference-service/src/providers/ollama.js @@ -1,17 +1,33 @@ const { Ollama } = require('ollama'); -const { getEnv } = require('@nexusai/shared'); +const { getEnv, OLLAMA, INFERENCE_DEFAULTS } = require('@nexusai/shared'); -const client = new Ollama({ host: getEnv('INFERENCE_URL', 'http://localhost:11434') }); -const DEFAULT_MODEL = getEnv('DEFAULT_MODEL', 'companion:latest'); +const client = new Ollama({ host: getEnv('INFERENCE_URL', OLLAMA.DEFAULT_URL) }); +const DEFAULT_MODEL = getEnv('DEFAULT_MODEL', OLLAMA.OLLAMA_MODEL); + +function resolveOptions(options){ + return { + temperature: options.temperature ?? INFERENCE_DEFAULTS.TEMPERATURE, + maxTokens: options.maxTokens ?? INFERENCE_DEFAULTS.MAX_TOKENS, + topP: options.topP ?? INFERENCE_DEFAULTS.TOP_P, + topK: options.topK ?? INFERENCE_DEFAULTS.TOP_K, + repeatPenalty: options.repeatPenalty ?? INFERENCE_DEFAULTS.REPEAT_PENALTY, + seed: options.seed ?? INFERENCE_DEFAULTS.SEED, + } +} async function complete(prompt, options = {} ) { + const opts = resolveOptions(options); const response = await client.generate({ model: options.model || DEFAULT_MODEL, prompt, stream: false, options: { - temperature: options.temperature ?? 0.7, - num_predict: options.maxTokens ?? 1024, + temperature: opts.temperature, + num_predict: opts.maxTokens, + top_p: opts.topP, + top_k: opts.topK, + repeat_penalty: opts.repeatPenalty, + ...(opts.seed !== null && { seed: opts.seed }), } }); @@ -25,12 +41,18 @@ async function complete(prompt, options = {} ) { } async function* completeStream(prompt, options = {} ) { + const opts = resolveOptions(options); const stream = await client.generate({ model: options.model || DEFAULT_MODEL, prompt, stream: true, options:{ - temperature: options.temperature ?? 0.7, + temperature: opts.temperature, + num_predict: opts.maxTokens, + top_p: opts.topP, + top_k: opts.topK, + repeat_penalty: opts.repeatPenalty, + ...(opts.seed !== null && { seed: opts.seed }), }, }); diff --git a/packages/memory-service/src/db/index.js b/packages/memory-service/src/db/index.js index da63c86..bac3002 100644 --- a/packages/memory-service/src/db/index.js +++ b/packages/memory-service/src/db/index.js @@ -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'); diff --git a/packages/memory-service/src/entities/index.js b/packages/memory-service/src/entities/index.js index b4b7528..e780c93 100644 --- a/packages/memory-service/src/entities/index.js +++ b/packages/memory-service/src/entities/index.js @@ -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, diff --git a/packages/memory-service/src/episodic/index.js b/packages/memory-service/src/episodic/index.js index cd4cefe..d498d7a 100644 --- a/packages/memory-service/src/episodic/index.js +++ b/packages/memory-service/src/episodic/index.js @@ -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', diff --git a/packages/memory-service/src/index.js b/packages/memory-service/src/index.js index c496d6a..09796d3 100644 --- a/packages/memory-service/src/index.js +++ b/packages/memory-service/src/index.js @@ -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), diff --git a/packages/orchestration-service/src/chat/index.js b/packages/orchestration-service/src/chat/index.js index 3f7ef2e..0b4f68a 100644 --- a/packages/orchestration-service/src/chat/index.js +++ b/packages/orchestration-service/src/chat/index.js @@ -2,14 +2,9 @@ const memory = require('../services/memory'); const inference = require('../services/inference'); const embedding = require('../services/embedding'); const qdrant = require('../services/qdrant'); +const { ORCHESTRATION } = require('@nexusai/shared') -const SYSTEM_PROMPT = `You are a helpful, context-aware AI assistant. -You have access to memories of past conversations with the user. -Use them to provide consistent, personalised responses.`; - -const RECENT_EPISODE_LIMIT = 5; // Number of recent episodes to retrieve for context -const SEMANTIC_LIMIT = 5; -const SCORE_THRESHOLD = 0.75; +const { RECENT_EPISODE_LIMIT, SEMANTIC_LIMIT, SCORE_THRESHOLD, SYSTEM_PROMPT } = ORCHESTRATION; function buildPrompt(recentEpisodes, semanticEpisodes, userMessage) { const parts = [SYSTEM_PROMPT]; diff --git a/packages/orchestration-service/src/index.js b/packages/orchestration-service/src/index.js index 1702ace..a4e3855 100644 --- a/packages/orchestration-service/src/index.js +++ b/packages/orchestration-service/src/index.js @@ -1,6 +1,6 @@ require ('dotenv').config(); const express = require('express'); -const {getEnv} = require('@nexusai/shared'); +const {getEnv, PORTS, SERVICES, ORCHESTRATION} = require('@nexusai/shared'); const chatRouter = require('./routes/chat'); const sessionsRouter = require('./routes/sessions'); const cors = require('cors'); @@ -8,25 +8,28 @@ const cors = require('cors'); const app = express(); app.use(express.json()); -const PORT = getEnv('PORT', '4000'); // Default to 4000 if PORT is not set - app.use(cors({ origin: [ - getEnv('CORS_ORIGIN', 'http://localhost:5173'), - 'http://localhost:5173', + getEnv('CORS_ORIGIN', ORCHESTRATION.CORS_ORIGIN), + ORCHESTRATION.CORS_ORIGIN, ], methods: ['GET', 'POST', 'DELETE'], allowedHeaders: ['Content-Type'], })) +const PORT = getEnv('PORT', PORTS.ORCHESTRATION); +const MEMORY_URL = getEnv('MEMORY_SERVICE_URL', SERVICES.MEMORY_URL); +const EMBEDDING_URL = getEnv('EMBEDDING_SERVICE_URL', SERVICES.EMBEDDING_URL); +const INFERENCE_URL = getEnv('INFERENCE_SERVICE_URL', SERVICES.INFERENCE_URL); + // Health check endpoint app.get('/health', (req, res) => { res.json({ - service: 'Orchestration Service', - status: 'healthy', - memoryService: getEnv('MEMORY_SERVICE_URL', 'http://localhost:3002'), - embeddingService: getEnv('EMBEDDING_SERVICE_URL', 'http://localhost:3003'), - inferenceService: getEnv('INFERENCE_SERVICE_URL', 'http://localhost:3001'), + service: 'Orchestration Service', + status: 'healthy', + memoryService: MEMORY_URL, + embeddingService: EMBEDDING_URL, + inferenceService: INFERENCE_URL, }); }); diff --git a/packages/orchestration-service/src/routes/sessions.js b/packages/orchestration-service/src/routes/sessions.js index 2142e9a..4068321 100644 --- a/packages/orchestration-service/src/routes/sessions.js +++ b/packages/orchestration-service/src/routes/sessions.js @@ -1,11 +1,12 @@ const { Router } = require('express'); const memory = require('../services/memory'); +const { EPISODIC } = require('@nexusai/shared'); const router = Router(); router.get('/:sessionId/history', async (req, res) => { const { sessionId } = req.params; - const { limit = 20, offset = 0 } = req.query; + const { limit = EPISODIC.DEFAULT_PAGE_SIZE, offset = EPISODIC.DEFAULT_OFFSET } = req.query; try { const session = await memory.getSessionByExternalId(sessionId); @@ -19,7 +20,7 @@ router.get('/:sessionId/history', async (req, res) => { }); router.get('/', async (req, res) => { - const {limit = 20, offset = 0 } = req.query; + const { limit = EPISODIC.DEFAULT_PAGE_SIZE, offset = EPISODIC.DEFAULT_OFFSET } = req.query; try { const sessions = await memory.getSessions(Number(limit), Number(offset)); res.json(sessions); diff --git a/packages/orchestration-service/src/services/inference.js b/packages/orchestration-service/src/services/inference.js index 2363b16..1684e46 100644 --- a/packages/orchestration-service/src/services/inference.js +++ b/packages/orchestration-service/src/services/inference.js @@ -1,7 +1,6 @@ -const fetch = require('node-fetch'); -const { getEnv } = require('@nexusai/shared'); +const { getEnv, SERVICES } = require('@nexusai/shared'); -const BASE_URL = getEnv('INFERENCE_SERVICE_URL', 'http://localhost:3001'); +const BASE_URL = getEnv('INFERENCE_SERVICE_URL', SERVICES.INFERENCE_URL); async function complete(prompt, options ={}) { const res = await fetch(`${BASE_URL}/complete`, { diff --git a/packages/orchestration-service/src/services/memory.js b/packages/orchestration-service/src/services/memory.js index af5a804..a59e246 100644 --- a/packages/orchestration-service/src/services/memory.js +++ b/packages/orchestration-service/src/services/memory.js @@ -1,7 +1,6 @@ -const fetch = require('node-fetch'); -const { getEnv } = require('@nexusai/shared'); +const { getEnv, SERVICES, EPISODIC } = require('@nexusai/shared'); -const BASE_URL = getEnv('MEMORY_SERVICE_URL', 'http://localhost:3002'); +const BASE_URL = getEnv('MEMORY_SERVICE_URL', SERVICES.MEMORY_URL); //function to get session by external id, returns null if not found, throws error for other issues async function getSessionByExternalId(externalId) { @@ -24,7 +23,7 @@ async function createSession(externalId) { return res.json(); } -async function getRecentEpisodes(sessionId, limit = 10) { +async function getRecentEpisodes(sessionId, limit = EPISODIC.DEFAULT_SESSIONS_LIMIT) { const res = await fetch(`${BASE_URL}/sessions/${sessionId}/episodes?limit=${limit}`); if (!res.ok) throw new Error(`Failed to fetch episodes: ${res.status} ${res.statusText}`); return res.json(); @@ -47,7 +46,7 @@ async function getEpisodeById(episodeId) { return res.json(); } -async function getSessionHistory(sessionId, limit = 20, offset = 0) { +async function getSessionHistory(sessionId, limit = EPISODIC.DEFAULT_SESSIONS_LIMIT, offset = EPISODIC.DEFAULT_OFFSET) { const res = await fetch( `${BASE_URL}/sessions/${sessionId}/episodes?limit=${limit}&offset=${offset}` ); @@ -56,9 +55,9 @@ async function getSessionHistory(sessionId, limit = 20, offset = 0) { return res.json(); } -async function getSessions(limit = 20, offset = 0) { +async function getSessions(limit = EPISODIC.DEFAULT_SESSIONS_LIMIT, offset = EPISODIC.DEFAULT_OFFSET) { const res = await fetch( - `${BASE_URL}/sessions?limit${limit}&offset=${offset}` + `${BASE_URL}/sessions?limit=${limit}&offset=${offset}` ); if (!res.ok) throw new Error(`Failed to fetch sessions: ${res.status}`); return res.json(); diff --git a/packages/orchestration-service/src/services/qdrant.js b/packages/orchestration-service/src/services/qdrant.js index b159dee..53b542c 100644 --- a/packages/orchestration-service/src/services/qdrant.js +++ b/packages/orchestration-service/src/services/qdrant.js @@ -1,8 +1,8 @@ -const {getEnv, QDRANT, COLLECTIONS } = require('@nexusai/shared') +const {getEnv, QDRANT, COLLECTIONS, ORCHESTRATION } = require('@nexusai/shared') const BASE_URL = getEnv('QDRANT_URL', QDRANT.DEFAULT_URL); -async function searchEpisodes( vector, {limit = 5, scoreThreshold = 0.75, sessionId } = {}) { +async function searchEpisodes( vector, {limit = ORCHESTRATION.RECENT_EPISODE_LIMIT, scoreThreshold = ORCHESTRATION.SCORE_THRESHOLD, sessionId } = {}) { const body = {vector, limit, score_threshold: scoreThreshold, with_payload: true}; if (sessionId) { diff --git a/packages/shared/src/config/constants.js b/packages/shared/src/config/constants.js index b6bf149..eb1715c 100644 --- a/packages/shared/src/config/constants.js +++ b/packages/shared/src/config/constants.js @@ -17,15 +17,64 @@ const EPISODIC = { DEFAULT_RECENT_LIMIT: 10, // Default number of recent episodes to retrieve DEFAULT_PAGE_SIZE: 20, // Default number of episodes per page for pagination DEFAULT_SEARCH_LIMIT: 10, // Default number of search results to return + DEFAULT_OFFSET: 0, + DEFAULT_SESSIONS_LIMIT: 20, +}; + +const ORCHESTRATION = { + RECENT_EPISODE_LIMIT: 5, + SEMANTIC_LIMIT: 5, + SCORE_THRESHOLD: 0.75, + CORS_ORIGIN: 'http://localhost:5173', + SYSTEM_PROMPT: `You are a helpful, context-aware AI assistant. You have access to memories of past conversations with the user. Use them to provide consistent, personalised responses.` +} + +const OLLAMA = { + DEFAULT_URL: 'http://localhost:11434', + EMBED_MODEL: 'nomic-embed-text', + OLLAMA_MODEL: 'companion:latest', +}; + +const LLAMACPP = { + DEFAULT_URL: 'http://localhost:8080', + DEFAULT_MODEL: 'local-model', +} + +const PORTS = { + INFERENCE: '3001', + MEMORY: '3002', + EMBEDDING: '3003', + ORCHESTRATION: '4000', }; const SERVICES = { - EMBEDDING_URL: 'http://localhost:3003' + EMBEDDING_URL: `http://localhost:${PORTS.EMBEDDING}`, + MEMORY_URL: `http://localhost:${PORTS.MEMORY}`, + INFERENCE_URL: `http://localhost:${PORTS.INFERENCE}`, }; +const INFERENCE_DEFAULTS = { + TEMPERATURE: 0.7, // Controls randomness. 0 = deterministic, 1 = creative + MAX_TOKENS: 1024, // Max tokens to generate in a response + TOP_P: 0.9, // Nucleus sampling — considers tokens comprising top 90% probability mass + TOP_K: 40, // Limits token selection to top K candidates at each step + REPEAT_PENALTY: 1.1, // Penalizes recently used tokens to reduce repetition + SEED: null, // null = random. Set to an integer for reproducible outputs +}; + +const SQLITE = { + DEFAULT_PATH: './data/nexusai.db' +} + module.exports = { QDRANT, COLLECTIONS, EPISODIC, - SERVICES + SERVICES, + OLLAMA, + PORTS, + LLAMACPP, + INFERENCE_DEFAULTS, + SQLITE, + ORCHESTRATION }; \ No newline at end of file diff --git a/packages/shared/src/index.js b/packages/shared/src/index.js index b8c5510..6740237 100644 --- a/packages/shared/src/index.js +++ b/packages/shared/src/index.js @@ -1,4 +1,19 @@ const {getEnv} = require('./config/env'); -const {QDRANT, COLLECTIONS, EPISODIC, SERVICES } = require('./config/constants'); +const {QDRANT, COLLECTIONS, EPISODIC, SERVICES, OLLAMA, PORTS, LLAMACPP, INFERENCE_DEFAULTS, SQLITE, ORCHESTRATION } = require('./config/constants'); +const {parseRow, formatEpisodeText} = require('./utils') -module.exports = {getEnv, QDRANT, COLLECTIONS, EPISODIC, SERVICES}; \ No newline at end of file +module.exports = { + getEnv, + QDRANT, + COLLECTIONS, + EPISODIC, + SERVICES, + OLLAMA, + PORTS, + LLAMACPP, + INFERENCE_DEFAULTS, + SQLITE, + ORCHESTRATION, + parseRow, + formatEpisodeText, +}; \ No newline at end of file diff --git a/packages/shared/src/utils.js b/packages/shared/src/utils.js new file mode 100644 index 0000000..68e97ee --- /dev/null +++ b/packages/shared/src/utils.js @@ -0,0 +1,13 @@ +function parseRow(row) { + if (!row) return null; + return { + ...row, + metadata: row.metadata ? JSON.parse(row.metadata) : null + }; +} + +function formatEpisodeText(userMessage, aiResponse) { + return `User: ${userMessage}\nAssistant: ${aiResponse}`; +} + +module.exports = { parseRow, formatEpisodeText }; \ No newline at end of file diff --git a/packages/source.zip b/packages/source.zip new file mode 100644 index 0000000..bc04ddd Binary files /dev/null and b/packages/source.zip differ