/** * Agent engine: context builder + toolchain orchestrator * * Builds the message context for vLLM inference: * - No system prompt (model trained on real conversations) * - Previous conversation memory (mother tongue imprint) * - Tool results (Notion, repo) injected as user/assistant messages * - Auto conversation rotation at threshold */ 'use strict'; const vllm = require('./vllm-proxy'); const sessionStore = require('./session-store'); const memoryAgent = require('./memory-agent'); const toolRegistry = require('./tool-registry'); const MAX_MESSAGES_BEFORE_ROTATE = 50; const MAX_CONTEXT_TOKENS = 3072; /** * Build the full message context for vLLM * * Context structure: * [ * {role:'user', content:'[Mother Tongue Imprint from last session]'}, * {role:'assistant', content:'[Understood. Continuing from where we left off.]'}, * ...previous messages, * {role:'user', content:'current message'} * ] * * NO system prompt - the model was trained on real conversations without system prompts. */ function buildContext(userHash, slotIndex, newMessage, conversationHistory, options = {}) { const messages = []; // 1. Cross-session memory imprint (if exists) const imprint = sessionStore.getLatestImprint(userHash); if (imprint) { messages.push({ role: 'user', content: '[Previous conversation memory]\n' + imprint }); messages.push({ role: 'assistant', content: '[I understand. I will continue based on our previous conversation.]' }); } // 2. Current conversation history const history = conversationHistory || []; for (const msg of history) { if (msg.role === 'system') continue; // Never inject system messages messages.push({ role: msg.role, content: msg.content || '' }); } // 3. Current user message messages.push({ role: 'user', content: newMessage || '' }); return messages; } /** * Process a chat message through the agent pipeline: * 1. Build context (memory + history) * 2. Send to vLLM * 3. Check if model requested a tool call * 4. If tool call: execute tool, inject result, re-request * 5. Return final response */ async function processMessage(userHash, slotIndex, message, conversationHistory = []) { const context = buildContext(userHash, slotIndex, message, conversationHistory); // Send to vLLM let response = await vllm.chatOnce(context, { maxTokens: 1024, temperature: 0.7 }); // Check for tool calls const toolCall = toolRegistry.parseToolCall(response); if (toolCall) { context.push({ role: 'assistant', content: response }); try { const toolResult = await toolRegistry.executeToolCall(toolCall); context.push({ role: 'user', content: '[Tool Result]\n' + JSON.stringify(toolResult, null, 2) }); // Re-request with tool result response = await vllm.chatOnce(context, { maxTokens: 1024, temperature: 0.7 }); } catch (err) { context.push({ role: 'user', content: '[Tool Error]\n' + err.message }); response = await vllm.chatOnce(context, { maxTokens: 512, temperature: 0.7 }); } } return { response, shouldRotate: conversationHistory.length >= MAX_MESSAGES_BEFORE_ROTATE }; } /** * Stream a chat message through the pipeline, writing SSE to res */ async function streamProcessMessage(userHash, slotIndex, message, conversationHistory, res) { const context = buildContext(userHash, slotIndex, message, conversationHistory); // Stream from vLLM vllm.pipeChat(context, res, { maxTokens: 1024, temperature: 0.7 }); return { shouldRotate: conversationHistory.length >= MAX_MESSAGES_BEFORE_ROTATE }; } function getMaxMessages() { return MAX_MESSAGES_BEFORE_ROTATE; } module.exports = { buildContext, processMessage, streamProcessMessage, getMaxMessages };