system prompt backend

This commit is contained in:
Storme-bit
2026-04-19 02:32:38 -07:00
parent 9c903a56ae
commit a0154e15e6
4 changed files with 25 additions and 27 deletions

View File

@@ -34,6 +34,10 @@ function getDB() {
db.exec(`ALTER TABLE projects ADD COLUMN notes TEXT`); // ← add this db.exec(`ALTER TABLE projects ADD COLUMN notes TEXT`); // ← add this
} catch {} } catch {}
try {
db.exec(`ALTER TABLE projects ADD COLUMN system_prompt TEXT`);
} catch {}
// Sync FTS index with any existing episodes data // Sync FTS index with any existing episodes data
db.exec(`INSERT OR REPLACE INTO episodes_fts(rowid, user_message, ai_response) db.exec(`INSERT OR REPLACE INTO episodes_fts(rowid, user_message, ai_response)
SELECT id, user_message, ai_response FROM episodes`); SELECT id, user_message, ai_response FROM episodes`);

View File

@@ -4,10 +4,9 @@ const embedding = require("../services/embedding");
const qdrant = require("../services/qdrant"); const qdrant = require("../services/qdrant");
const { ORCHESTRATION } = require("@nexusai/shared"); const { ORCHESTRATION } = require("@nexusai/shared");
const appSettings = require("../config/settings"); const appSettings = require("../config/settings");
const { SYSTEM_PROMPT } = ORCHESTRATION;
function buildPrompt(recentEpisodes, semanticEpisodes, entities, userMessage) { function buildPrompt(recentEpisodes, semanticEpisodes, entities, userMessage, systemPrompt) {
const parts = [SYSTEM_PROMPT]; const parts = [systemPrompt ?? ORCHESTRATION.SYSTEM_PROMPT];
if (entities.length > 0) { if (entities.length > 0) {
parts.push( parts.push(
@@ -126,30 +125,21 @@ async function getRelevantEntities(userMessage, projectId=null) {
} }
async function chat(externalId, userMessage, options = {}) { 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(); appSettings.load();
// 1. Resolve or create session // 1. Resolve or create session
let session = await memory.getSessionByExternalId(externalId); let session = await memory.getSessionByExternalId(externalId);
if (!session) session = await memory.createSession(externalId); if (!session) session = await memory.createSession(externalId);
let projectSessionIds = null; let projectSessionIds = null;
let activeSystemPrompt = systemPrompt ?? ORCHESTRATION.SYSTEM_PROMPT;
if (session.project_id) { if (session.project_id) {
try { try {
const project = await memory.getProject(session.project_id); const project = await memory.getProject(session.project_id);
if (project) { if (project) {
const projectSessions = await memory.getProjectSessions( const projectSessions = await memory.getProjectSessions(session.project_id);
session.project_id, if (project?.system_prompt) activeSystemPrompt = project.system_prompt;
);
projectSessionIds = projectSessions.map((s) => s.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`,
);
}
} }
} catch (err) { } catch (err) {
console.warn( console.warn(
@@ -184,6 +174,7 @@ async function chat(externalId, userMessage, options = {}) {
semanticEpisodes, semanticEpisodes,
entities, entities,
userMessage, userMessage,
activeSystemPrompt,
); );
// 5. Run inference // 5. Run inference
@@ -218,11 +209,12 @@ async function chat(externalId, userMessage, options = {}) {
async function chatStream(externalId, userMessage, onChunk, options = {}) { async function chatStream(externalId, userMessage, onChunk, options = {}) {
try { 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); let session = await memory.getSessionByExternalId(externalId);
if (!session) session = await memory.createSession(externalId); if (!session) session = await memory.createSession(externalId);
let projectSessionIds = null; let projectSessionIds = null;
let activeSystemPrompt = systemPrompt ?? ORCHESTRATION.SYSTEM_PROMPT;
if (session.project_id) { if (session.project_id) {
try { try {
const project = await memory.getProject(session.project_id); const project = await memory.getProject(session.project_id);
@@ -231,16 +223,9 @@ async function chatStream(externalId, userMessage, onChunk, options = {}) {
session.project_id, session.project_id,
); );
projectSessionIds = projectSessions.map((s) => s.id); projectSessionIds = projectSessions.map((s) => s.id);
if (project.isolated === 1) { if (project?.system_prompt) activeSystemPrompt = project.system_prompt;
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) { } catch (err) {
console.warn( console.warn(
"[orchestration] Failed to resolve project context:", "[orchestration] Failed to resolve project context:",
@@ -270,6 +255,7 @@ async function chatStream(externalId, userMessage, onChunk, options = {}) {
semanticEpisodes, semanticEpisodes,
entities, entities,
userMessage, userMessage,
activeSystemPrompt,
); );
const res = await inference.completeStream(prompt, {...options, temperature, repeatPenalty, topP, topK}); const res = await inference.completeStream(prompt, {...options, temperature, repeatPenalty, topP, topK});

View File

@@ -12,7 +12,8 @@ const DEFAULTS = {
temperature: INFERENCE_DEFAULTS.TEMPERATURE, temperature: INFERENCE_DEFAULTS.TEMPERATURE,
repeatPenalty: INFERENCE_DEFAULTS.REPEAT_PENALTY, repeatPenalty: INFERENCE_DEFAULTS.REPEAT_PENALTY,
topP: INFERENCE_DEFAULTS.TOP_P, topP: INFERENCE_DEFAULTS.TOP_P,
topK: INFERENCE_DEFAULTS.TOP_K topK: INFERENCE_DEFAULTS.TOP_K,
systemPrompt: ORCHESTRATION.SYSTEM_PROMPT,
}; };
function load() { function load() {

View File

@@ -73,6 +73,13 @@ if (req.body.topK !== undefined) {
updates.topK = val; 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)); res.json(settings.save(updates));
}); });