chat/index.js cleanup

This commit is contained in:
Storme-bit
2026-04-26 23:04:31 -07:00
parent 45db47a584
commit 696ead29f8

View File

@@ -117,7 +117,7 @@ async function getRelevantEntities(userMessage, projectId=null) {
); );
return results.map((r) => r.payload).filter(Boolean); return results.map((r) => r.payload).filter(Boolean);
} catch (err) { } catch (err) {
logger.warn( logger.debug(
"[orchestration] Entity search failed, continuing without:", "[orchestration] Entity search failed, continuing without:",
err.message, err.message,
); );
@@ -125,13 +125,16 @@ async function getRelevantEntities(userMessage, projectId=null) {
} }
} }
async function chat(externalId, userMessage, options = {}) { async function assembleContext(externalId, userMessage) {
const { recentEpisodeLimit, semanticLimit, scoreThreshold, temperature, repeatPenalty, topP, topK, systemPrompt} = const settings = appSettings.load();
appSettings.load(); const { recentEpisodeLimit, semanticLimit, scoreThreshold,
temperature, repeatPenalty, topP, topK, systemPrompt } = settings;
// 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);
// 2. Resolve project context
let projectSessionIds = null; let projectSessionIds = null;
let activeSystemPrompt = systemPrompt ?? ORCHESTRATION.SYSTEM_PROMPT; let activeSystemPrompt = systemPrompt ?? ORCHESTRATION.SYSTEM_PROMPT;
if (session.project_id) { if (session.project_id) {
@@ -139,49 +142,41 @@ async function chat(externalId, userMessage, options = {}) {
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(session.project_id); const projectSessions = await memory.getProjectSessions(session.project_id);
if (project?.system_prompt) activeSystemPrompt = project.system_prompt; if (project.system_prompt) activeSystemPrompt = project.system_prompt;
projectSessionIds = projectSessions.map((s) => s.id); projectSessionIds = projectSessions.map(s => s.id);
} }
} catch (err) { } catch (err) {
logger.warn( logger.warn('[orchestration] Failed to resolve project context:', err.message);
"[orchestration] Failed to resolve project context:",
err.message,
);
} }
} }
// 2. Fetch recent episodes for context
const recentEpisodes = await memory.getRecentEpisodes( // 3. Fetch recent episodes
session.id, const recentEpisodes = await memory.getRecentEpisodes(session.id, recentEpisodeLimit);
recentEpisodeLimit,
);
const isFirstMessage = recentEpisodes.length === 0; const isFirstMessage = recentEpisodes.length === 0;
const recentIds = new Set(recentEpisodes.map((e) => e.id)); const recentIds = new Set(recentEpisodes.map(e => e.id));
// 3. Semantic Search // 4. Semantic + entity search
const semanticEpisodes = await getSemanticEpisodes( const semanticEpisodes = await getSemanticEpisodes(
userMessage, userMessage, session.id, recentIds, projectSessionIds, { semanticLimit, scoreThreshold }
session.id,
recentIds,
projectSessionIds,
{ semanticLimit, scoreThreshold },
); );
// 3b. Entity Search
const entities = await getRelevantEntities(userMessage, session.project_id ?? null); const entities = await getRelevantEntities(userMessage, session.project_id ?? null);
// 4. Assemble prompt // 5. Assemble prompt
const prompt = buildPrompt( const prompt = buildPrompt(recentEpisodes, semanticEpisodes, entities, userMessage, activeSystemPrompt);
recentEpisodes,
semanticEpisodes,
entities,
userMessage,
activeSystemPrompt,
);
// 5. Run inference return {
const result = await inference.complete(prompt, {...options, temperature, repeatPenalty, topP, topK}); session,
prompt,
isFirstMessage,
inferenceOptions: { temperature, repeatPenalty, topP, topK },
};
}
async function chat(externalId, userMessage, options = {}) {
const { session, prompt, isFirstMessage, inferenceOptions } = await assembleContext(externalId, userMessage);
const result = await inference.complete(prompt, { ...options, ...inferenceOptions });
// 6. Write episode back to memory
try { try {
await memory.createEpisode( await memory.createEpisode(
session.id, userMessage, result.text, session.id, userMessage, result.text,
@@ -191,16 +186,14 @@ async function chat(externalId, userMessage, options = {}) {
} catch (err) { } catch (err) {
logger.error('[orchestration] Failed to save episode:', err.message); logger.error('[orchestration] Failed to save episode:', err.message);
} }
const allEpisodes = await memory.getRecentEpisodes(session.id, 9999); const allEpisodes = await memory.getRecentEpisodes(session.id, 9999);
triggerSummary(session, allEpisodes); triggerSummary(session, allEpisodes);
// 8. Auto-name on first message
if (isFirstMessage && !session.name) { if (isFirstMessage && !session.name) {
autoNameSession(externalId, userMessage, result.text).catch(() => {}); // already logged inside autoNameSession autoNameSession(externalId, userMessage, result.text).catch(() => {});
} }
// 9. Return response
return { return {
sessionId: externalId, sessionId: externalId,
response: result.text, response: result.text,
@@ -210,103 +203,35 @@ 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, systemPrompt } = appSettings.load(); const { session, prompt, isFirstMessage, inferenceOptions } = await assembleContext(externalId, userMessage);
let session = await memory.getSessionByExternalId(externalId);
if (!session) session = await memory.createSession(externalId);
let projectSessionIds = null; const res = await inference.completeStream(prompt, { ...options, ...inferenceOptions });
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,
);
projectSessionIds = projectSessions.map((s) => s.id);
if (project?.system_prompt) activeSystemPrompt = project.system_prompt;
}
} catch (err) { let fullText = '', model = '', tokenCount = 0, buffer = '';
logger.warn(
"[orchestration] Failed to resolve project context:",
err.message,
);
}
}
const recentEpisodes = await memory.getRecentEpisodes(
session.id,
recentEpisodeLimit,
);
const isFirstMessage = recentEpisodes.length === 0;
const recentIds = new Set(recentEpisodes.map((e) => e.id));
const semanticEpisodes = await getSemanticEpisodes(
userMessage,
session.id,
recentIds,
projectSessionIds,
{semanticLimit, scoreThreshold }
);
const entities = await getRelevantEntities(userMessage, session.project_id ?? null);
const prompt = buildPrompt(
recentEpisodes,
semanticEpisodes,
entities,
userMessage,
activeSystemPrompt,
);
const res = await inference.completeStream(prompt, {...options, temperature, repeatPenalty, topP, topK});
let fullText = "";
let model = "";
let tokenCount = 0;
let buffer = "";
for await (const chunk of res.body) { for await (const chunk of res.body) {
buffer += Buffer.from(chunk).toString("utf8"); buffer += Buffer.from(chunk).toString('utf8');
const events = buffer.split('\n\n');
const events = buffer.split("\n\n"); buffer = events.pop() || '';
buffer = events.pop() || "";
for (const event of events) { for (const event of events) {
const lines = event.split("\n"); const dataLines = event.split('\n')
const dataLines = lines .filter(line => line.startsWith('data: '))
.filter((line) => line.startsWith("data: ")) .map(line => line.slice(6));
.map((line) => line.slice(6));
if (dataLines.length === 0) continue; if (!dataLines.length) continue;
const raw = dataLines.join('\n').trim();
const raw = dataLines.join("\n").trim(); if (raw === '[DONE]') continue;
if (raw === "[DONE]") continue;
try { try {
const data = JSON.parse(raw); const data = JSON.parse(raw);
if (data.response) { fullText += data.response; onChunk(data.response); }
if (data.response) {
fullText += data.response;
onChunk(data.response);
}
if (data.model) model = data.model; if (data.model) model = data.model;
if (data.done && data.tokenCount !== undefined) { if (data.done && data.tokenCount !== undefined) tokenCount = data.tokenCount;
tokenCount = data.tokenCount; if (data.error) throw new Error(data.error);
}
if (data.error) {
throw new Error(data.error);
}
} catch (err) { } catch (err) {
logger.error( logger.error('[orchestration] Failed to parse SSE event:', raw, err.message);
"[orchestration] Failed to parse inference SSE event:",
raw,
err.message,
);
} }
} }
} }
@@ -316,9 +241,7 @@ async function chatStream(externalId, userMessage, onChunk, options = {}) {
const allEpisodes = await memory.getRecentEpisodes(session.id, 9999); const allEpisodes = await memory.getRecentEpisodes(session.id, 9999);
triggerSummary(session, allEpisodes); triggerSummary(session, allEpisodes);
} else { } else {
logger.warn( logger.warn('[orchestration] Stream finished with no assistant text; episode not saved');
"[orchestration] Stream finished with no assistant text; episode not saved",
);
} }
if (isFirstMessage && !session.name) { if (isFirstMessage && !session.name) {
@@ -327,11 +250,7 @@ async function chatStream(externalId, userMessage, onChunk, options = {}) {
return { model, tokenCount }; return { model, tokenCount };
} catch (err) { } catch (err) {
logger.error( logger.error('[orchestration] chatStream fatal error:', err.message, err.stack);
"[orchestration] chatStream fatal error:",
err.message,
err.stack,
);
throw err; throw err;
} }
} }