diff --git a/packages/memory-service/src/db/index.js b/packages/memory-service/src/db/index.js index a12d951..c370ec9 100644 --- a/packages/memory-service/src/db/index.js +++ b/packages/memory-service/src/db/index.js @@ -34,6 +34,10 @@ function getDB() { db.exec(`ALTER TABLE projects ADD COLUMN notes TEXT`); // ← add this } catch {} + try { + db.exec(`ALTER TABLE projects ADD COLUMN system_prompt TEXT`); + } catch {} + // Sync FTS index with any existing episodes data db.exec(`INSERT OR REPLACE INTO episodes_fts(rowid, user_message, ai_response) SELECT id, user_message, ai_response FROM episodes`); diff --git a/packages/orchestration-service/src/chat/index.js b/packages/orchestration-service/src/chat/index.js index fd4bd5d..d5ffbb2 100644 --- a/packages/orchestration-service/src/chat/index.js +++ b/packages/orchestration-service/src/chat/index.js @@ -4,10 +4,9 @@ const embedding = require("../services/embedding"); const qdrant = require("../services/qdrant"); const { ORCHESTRATION } = require("@nexusai/shared"); const appSettings = require("../config/settings"); -const { SYSTEM_PROMPT } = ORCHESTRATION; -function buildPrompt(recentEpisodes, semanticEpisodes, entities, userMessage) { - const parts = [SYSTEM_PROMPT]; +function buildPrompt(recentEpisodes, semanticEpisodes, entities, userMessage, systemPrompt) { + const parts = [systemPrompt ?? ORCHESTRATION.SYSTEM_PROMPT]; if (entities.length > 0) { parts.push( @@ -126,30 +125,21 @@ async function getRelevantEntities(userMessage, projectId=null) { } async function chat(externalId, userMessage, options = {}) { - const { recentEpisodeLimit, semanticLimit, scoreThreshold, temperature, repeatPenalty, topP, topK} = + const { recentEpisodeLimit, semanticLimit, scoreThreshold, temperature, repeatPenalty, topP, topK, systemPrompt} = appSettings.load(); // 1. Resolve or create session let session = await memory.getSessionByExternalId(externalId); if (!session) session = await memory.createSession(externalId); let projectSessionIds = null; + let activeSystemPrompt = systemPrompt ?? ORCHESTRATION.SYSTEM_PROMPT; if (session.project_id) { try { const project = await memory.getProject(session.project_id); if (project) { - const projectSessions = await memory.getProjectSessions( - session.project_id, - ); + const projectSessions = await memory.getProjectSessions(session.project_id); + if (project?.system_prompt) activeSystemPrompt = project.system_prompt; projectSessionIds = projectSessions.map((s) => s.id); - if (project.isolated === 1) { - console.log( - `[orchestration] Isolated project — restricting to ${projectSessionIds.length} sessions`, - ); - } else { - console.log( - `[orchestration] Non-isolated project — expanding search to ${projectSessionIds.length} sessions`, - ); - } } } catch (err) { console.warn( @@ -184,6 +174,7 @@ async function chat(externalId, userMessage, options = {}) { semanticEpisodes, entities, userMessage, + activeSystemPrompt, ); // 5. Run inference @@ -218,11 +209,12 @@ async function chat(externalId, userMessage, options = {}) { async function chatStream(externalId, userMessage, onChunk, options = {}) { try { - const { recentEpisodeLimit, semanticLimit, scoreThreshold, temperature, repeatPenalty, topP, topK } = appSettings.load(); + const { recentEpisodeLimit, semanticLimit, scoreThreshold, temperature, repeatPenalty, topP, topK, systemPrompt } = appSettings.load(); let session = await memory.getSessionByExternalId(externalId); if (!session) session = await memory.createSession(externalId); let projectSessionIds = null; + let activeSystemPrompt = systemPrompt ?? ORCHESTRATION.SYSTEM_PROMPT; if (session.project_id) { try { const project = await memory.getProject(session.project_id); @@ -231,16 +223,9 @@ async function chatStream(externalId, userMessage, onChunk, options = {}) { session.project_id, ); projectSessionIds = projectSessions.map((s) => s.id); - if (project.isolated === 1) { - console.log( - `[orchestration] Isolated project — restricting to ${projectSessionIds.length} sessions`, - ); - } else { - console.log( - `[orchestration] Non-isolated project — expanding search to ${projectSessionIds.length} sessions`, - ); - } + if (project?.system_prompt) activeSystemPrompt = project.system_prompt; } + } catch (err) { console.warn( "[orchestration] Failed to resolve project context:", @@ -270,6 +255,7 @@ async function chatStream(externalId, userMessage, onChunk, options = {}) { semanticEpisodes, entities, userMessage, + activeSystemPrompt, ); const res = await inference.completeStream(prompt, {...options, temperature, repeatPenalty, topP, topK}); diff --git a/packages/orchestration-service/src/config/settings.js b/packages/orchestration-service/src/config/settings.js index 045297d..6d3b5d4 100644 --- a/packages/orchestration-service/src/config/settings.js +++ b/packages/orchestration-service/src/config/settings.js @@ -12,7 +12,8 @@ const DEFAULTS = { temperature: INFERENCE_DEFAULTS.TEMPERATURE, repeatPenalty: INFERENCE_DEFAULTS.REPEAT_PENALTY, topP: INFERENCE_DEFAULTS.TOP_P, - topK: INFERENCE_DEFAULTS.TOP_K + topK: INFERENCE_DEFAULTS.TOP_K, + systemPrompt: ORCHESTRATION.SYSTEM_PROMPT, }; function load() { diff --git a/packages/orchestration-service/src/routes/settings.js b/packages/orchestration-service/src/routes/settings.js index 963a0a9..b2089cf 100644 --- a/packages/orchestration-service/src/routes/settings.js +++ b/packages/orchestration-service/src/routes/settings.js @@ -73,6 +73,13 @@ if (req.body.topK !== undefined) { updates.topK = val; } +if (req.body.systemPrompt !== undefined) { + const val = req.body.systemPrompt; + if (typeof val !== 'string') + return res.status(400).json({ error: 'systemPrompt must be a string' }); + updates.systemPrompt = val.trim() || null; // null reverts to default +} + res.json(settings.save(updates)); });