UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

150 lines 6.52 kB
/** * Shared Summarization Engine * * Extracted from ConversationMemoryManager and RedisConversationMemoryManager * to eliminate code duplication. Both managers delegate to this engine. */ import { TokenUtils } from "../constants/tokens.js"; import { buildContextFromPointer, generateSummary, } from "../utils/conversationMemory.js"; import { RECENT_MESSAGES_RATIO } from "../config/conversationMemory.js"; import { withSpan } from "../telemetry/withSpan.js"; import { tracers } from "../telemetry/tracers.js"; import { logger } from "../utils/logger.js"; /** * Centralized summarization engine for conversation memory. * Handles token counting, threshold checking, and summary generation. */ export class SummarizationEngine { /** * Check if a session needs summarization and perform it if so. * @param session - Session memory to check and potentially summarize * @param threshold - Token threshold that triggers summarization * @param config - Conversation memory configuration (partial allowed) * @param logPrefix - Prefix for log messages * @returns True if summarization was performed */ async checkAndSummarize(session, threshold, config, logPrefix = "[SummarizationEngine]", requestId) { return withSpan({ name: "neurolink.memory.summarize", tracer: tracers.memory, attributes: { "memory.session_id": session.sessionId ?? "unknown", "memory.threshold": threshold, }, }, async (span) => { const contextMessages = buildContextFromPointer(session, requestId); const tokenCount = this.estimateTokens(contextMessages); session.lastTokenCount = tokenCount; session.lastCountedAt = Date.now(); logger.info("[Summarization] Check", { requestId, sessionId: session.sessionId, tokenCount, threshold, willSummarize: tokenCount >= threshold, }); if (tokenCount >= threshold) { await this.summarizeSession(session, threshold, config, logPrefix, requestId); span.setAttribute("memory.summarized", true); return true; } span.setAttribute("memory.summarized", false); return false; }); // end withSpan } /** * Perform token-based summarization on a session. * Uses pointer-based, non-destructive approach. * @param session - Session memory to summarize * @param threshold - Token threshold for calculating split point * @param config - Conversation memory configuration (partial allowed) * @param logPrefix - Prefix for log messages */ async summarizeSession(session, threshold, config, logPrefix = "[SummarizationEngine]", requestId) { const startTime = Date.now(); const startIndex = session.summarizedUpToMessageId ? session.messages.findIndex((m) => m.id === session.summarizedUpToMessageId) + 1 : 0; const recentMessages = session.messages.slice(startIndex); if (recentMessages.length === 0) { return; } const targetRecentTokens = threshold * RECENT_MESSAGES_RATIO; const splitIndex = this.findSplitIndexByTokens(recentMessages, targetRecentTokens); const messagesToSummarize = recentMessages.slice(0, splitIndex); if (messagesToSummarize.length === 0) { return; } const recentToKeep = recentMessages.length - messagesToSummarize.length; logger.info("[Summarization] Starting", { requestId, sessionId: session.sessionId, messagesToSummarize: messagesToSummarize.length, recentToKeep, hasPreviousSummary: !!session.summarizedMessage, }); try { const summary = await generateSummary(messagesToSummarize, config, logPrefix, session.summarizedMessage, requestId); if (!summary) { logger.warn(`${logPrefix} Summary generation failed`, { requestId, sessionId: session.sessionId, durationMs: Date.now() - startTime, }); return; } const lastSummarized = messagesToSummarize[messagesToSummarize.length - 1]; session.summarizedUpToMessageId = lastSummarized.id; session.summarizedMessage = summary; logger.info("[Summarization] Complete", { requestId, sessionId: session.sessionId, summaryChars: summary.length, newPointerId: lastSummarized.id, durationMs: Date.now() - startTime, }); } catch (err) { const errorMessage = err instanceof Error ? err.message : String(err); logger.error("[Summarization] Error", { requestId, sessionId: session.sessionId, error: errorMessage, durationMs: Date.now() - startTime, }); throw err; } } /** * Estimate total tokens for a message array. * @param messages - Array of chat messages * @returns Estimated token count */ estimateTokens(messages) { return messages.reduce((total, msg) => { return total + TokenUtils.estimateTokenCount(msg.content); }, 0); } /** * Find split index to keep recent messages within target token count. * Works backwards from the most recent message to find the split point. * @param messages - Array of messages to analyze * @param targetRecentTokens - Target token count for recent messages * @returns Index at which to split (messages before this index will be summarized) */ findSplitIndexByTokens(messages, targetRecentTokens) { let recentTokens = 0; let splitIndex = messages.length; for (let i = messages.length - 1; i >= 0; i--) { const msgTokens = TokenUtils.estimateTokenCount(messages[i].content); if (recentTokens + msgTokens > targetRecentTokens) { splitIndex = i + 1; break; } recentTokens += msgTokens; } // Ensure at least one message is summarized return Math.max(1, splitIndex); } } //# sourceMappingURL=summarizationEngine.js.map