@juspay/neurolink 7.29.3 → 7.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/config/conversationMemoryConfig.js +5 -0
- package/dist/core/conversationMemoryManager.d.ts +9 -15
- package/dist/core/conversationMemoryManager.js +98 -57
- package/dist/core/types.d.ts +3 -1
- package/dist/lib/config/conversationMemoryConfig.js +5 -0
- package/dist/lib/core/conversationMemoryManager.d.ts +9 -15
- package/dist/lib/core/conversationMemoryManager.js +98 -57
- package/dist/lib/core/types.d.ts +3 -1
- package/dist/lib/neurolink.d.ts +0 -9
- package/dist/lib/neurolink.js +7 -39
- package/dist/lib/types/conversationTypes.d.ts +10 -0
- package/dist/lib/types/generateTypes.d.ts +1 -2
- package/dist/lib/utils/conversationMemoryUtils.d.ts +1 -2
- package/dist/lib/utils/conversationMemoryUtils.js +6 -7
- package/dist/neurolink.d.ts +0 -9
- package/dist/neurolink.js +7 -39
- package/dist/types/conversationTypes.d.ts +10 -0
- package/dist/types/generateTypes.d.ts +1 -2
- package/dist/utils/conversationMemoryUtils.d.ts +1 -2
- package/dist/utils/conversationMemoryUtils.js +6 -7
- package/package.json +1 -1
- package/dist/context/ContextManager.d.ts +0 -28
- package/dist/context/ContextManager.js +0 -113
- package/dist/context/config.d.ts +0 -5
- package/dist/context/config.js +0 -42
- package/dist/context/types.d.ts +0 -20
- package/dist/context/types.js +0 -1
- package/dist/context/utils.d.ts +0 -7
- package/dist/context/utils.js +0 -8
- package/dist/lib/context/ContextManager.d.ts +0 -28
- package/dist/lib/context/ContextManager.js +0 -113
- package/dist/lib/context/config.d.ts +0 -5
- package/dist/lib/context/config.js +0 -42
- package/dist/lib/context/types.d.ts +0 -20
- package/dist/lib/context/types.js +0 -1
- package/dist/lib/context/utils.d.ts +0 -7
- package/dist/lib/context/utils.js +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [7.30.0](https://github.com/juspay/neurolink/compare/v7.29.3...v7.30.0) (2025-08-29)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
- **(SDK):** Integrate context summarization with conversation memory BZ-43344 ([a2316ff](https://github.com/juspay/neurolink/commit/a2316ff6df55107316892d33365455e6ebdcbbd9))
|
|
6
|
+
|
|
1
7
|
## [7.29.3](https://github.com/juspay/neurolink/compare/v7.29.2...v7.29.3) (2025-08-29)
|
|
2
8
|
|
|
3
9
|
### Bug Fixes
|
|
@@ -36,5 +36,10 @@ export function getConversationMemoryDefaults() {
|
|
|
36
36
|
maxSessions: Number(process.env.NEUROLINK_MEMORY_MAX_SESSIONS) || DEFAULT_MAX_SESSIONS,
|
|
37
37
|
maxTurnsPerSession: Number(process.env.NEUROLINK_MEMORY_MAX_TURNS_PER_SESSION) ||
|
|
38
38
|
DEFAULT_MAX_TURNS_PER_SESSION,
|
|
39
|
+
enableSummarization: process.env.NEUROLINK_SUMMARIZATION_ENABLED === "true",
|
|
40
|
+
summarizationThresholdTurns: Number(process.env.NEUROLINK_SUMMARIZATION_THRESHOLD_TURNS) || 20,
|
|
41
|
+
summarizationTargetTurns: Number(process.env.NEUROLINK_SUMMARIZATION_TARGET_TURNS) || 10,
|
|
42
|
+
summarizationProvider: process.env.NEUROLINK_SUMMARIZATION_PROVIDER || "vertex",
|
|
43
|
+
summarizationModel: process.env.NEUROLINK_SUMMARIZATION_MODEL || "gemini-2.5-flash",
|
|
39
44
|
};
|
|
40
45
|
}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Conversation Memory Manager for NeuroLink
|
|
3
3
|
* Handles in-memory conversation storage, session management, and context injection
|
|
4
4
|
*/
|
|
5
|
-
import type { ConversationMemoryConfig, ConversationMemoryStats, ChatMessage } from "../types/conversationTypes.js";
|
|
5
|
+
import type { ConversationMemoryConfig, SessionMemory, ConversationMemoryStats, ChatMessage } from "../types/conversationTypes.js";
|
|
6
6
|
export declare class ConversationMemoryManager {
|
|
7
7
|
private sessions;
|
|
8
|
-
|
|
8
|
+
config: ConversationMemoryConfig;
|
|
9
9
|
private isInitialized;
|
|
10
10
|
constructor(config: ConversationMemoryConfig);
|
|
11
11
|
/**
|
|
@@ -22,20 +22,14 @@ export declare class ConversationMemoryManager {
|
|
|
22
22
|
* Returns pre-stored message array with zero conversion overhead
|
|
23
23
|
*/
|
|
24
24
|
buildContextMessages(sessionId: string): ChatMessage[];
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
getStats(): Promise<ConversationMemoryStats>;
|
|
30
|
-
/**
|
|
31
|
-
* Clear all conversations for a specific session
|
|
32
|
-
*/
|
|
33
|
-
clearSession(sessionId: string): Promise<boolean>;
|
|
34
|
-
/**
|
|
35
|
-
* Clear all conversations (reset memory)
|
|
36
|
-
*/
|
|
37
|
-
clearAllSessions(): Promise<void>;
|
|
25
|
+
getSession(sessionId: string): SessionMemory | undefined;
|
|
26
|
+
createSummarySystemMessage(content: string): ChatMessage;
|
|
27
|
+
private _summarizeSession;
|
|
28
|
+
private _createSummarizationPrompt;
|
|
38
29
|
private ensureInitialized;
|
|
39
30
|
private createNewSession;
|
|
40
31
|
private enforceSessionLimit;
|
|
32
|
+
getStats(): Promise<ConversationMemoryStats>;
|
|
33
|
+
clearSession(sessionId: string): Promise<boolean>;
|
|
34
|
+
clearAllSessions(): Promise<void>;
|
|
41
35
|
}
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
* Handles in-memory conversation storage, session management, and context injection
|
|
4
4
|
*/
|
|
5
5
|
import { ConversationMemoryError } from "../types/conversationTypes.js";
|
|
6
|
-
import { DEFAULT_MAX_TURNS_PER_SESSION, DEFAULT_MAX_SESSIONS, MESSAGES_PER_TURN
|
|
6
|
+
import { DEFAULT_MAX_TURNS_PER_SESSION, DEFAULT_MAX_SESSIONS, MESSAGES_PER_TURN } from "../config/conversationMemoryConfig.js";
|
|
7
7
|
import { logger } from "../utils/logger.js";
|
|
8
|
+
import { NeuroLink } from "../neurolink.js";
|
|
8
9
|
export class ConversationMemoryManager {
|
|
9
10
|
sessions = new Map();
|
|
10
11
|
config;
|
|
11
12
|
isInitialized = false;
|
|
12
13
|
constructor(config) {
|
|
13
|
-
// Trust that config is already complete from applyConversationMemoryDefaults()
|
|
14
14
|
this.config = config;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
@@ -48,21 +48,19 @@ export class ConversationMemoryManager {
|
|
|
48
48
|
// ULTRA-OPTIMIZED: Direct message storage - no intermediate objects
|
|
49
49
|
session.messages.push({ role: "user", content: userMessage }, { role: "assistant", content: aiResponse });
|
|
50
50
|
session.lastActivity = Date.now();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
if (this.config.enableSummarization) {
|
|
52
|
+
const currentTurnCount = session.messages.length / MESSAGES_PER_TURN;
|
|
53
|
+
if (currentTurnCount > (this.config.summarizationThresholdTurns || 20)) {
|
|
54
|
+
await this._summarizeSession(session);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const maxMessages = (this.config.maxTurnsPerSession || DEFAULT_MAX_TURNS_PER_SESSION) * MESSAGES_PER_TURN;
|
|
59
|
+
if (session.messages.length > maxMessages) {
|
|
60
|
+
session.messages = session.messages.slice(-maxMessages);
|
|
61
|
+
}
|
|
56
62
|
}
|
|
57
|
-
// Enforce global session limit
|
|
58
63
|
this.enforceSessionLimit();
|
|
59
|
-
logger.debug("Conversation turn stored", {
|
|
60
|
-
sessionId,
|
|
61
|
-
messageCount: session.messages.length,
|
|
62
|
-
turnCount: session.messages.length / MESSAGES_PER_TURN, // Each turn = MESSAGES_PER_TURN messages
|
|
63
|
-
userMessageLength: userMessage.length,
|
|
64
|
-
aiResponseLength: aiResponse.length,
|
|
65
|
-
});
|
|
66
64
|
}
|
|
67
65
|
catch (error) {
|
|
68
66
|
throw new ConversationMemoryError(`Failed to store conversation turn for session ${sessionId}`, "STORAGE_ERROR", {
|
|
@@ -77,48 +75,74 @@ export class ConversationMemoryManager {
|
|
|
77
75
|
*/
|
|
78
76
|
buildContextMessages(sessionId) {
|
|
79
77
|
const session = this.sessions.get(sessionId);
|
|
80
|
-
|
|
81
|
-
return [];
|
|
82
|
-
}
|
|
83
|
-
// ULTRA-OPTIMIZED: Direct return - no processing needed!
|
|
84
|
-
return session.messages;
|
|
78
|
+
return session ? session.messages : [];
|
|
85
79
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
async getStats() {
|
|
91
|
-
await this.ensureInitialized();
|
|
92
|
-
const sessions = Array.from(this.sessions.values());
|
|
93
|
-
const totalTurns = sessions.reduce((sum, session) => sum + session.messages.length / MESSAGES_PER_TURN, 0);
|
|
80
|
+
getSession(sessionId) {
|
|
81
|
+
return this.sessions.get(sessionId);
|
|
82
|
+
}
|
|
83
|
+
createSummarySystemMessage(content) {
|
|
94
84
|
return {
|
|
95
|
-
|
|
96
|
-
|
|
85
|
+
role: "system",
|
|
86
|
+
content: `Summary of previous conversation turns:\n\n${content}`,
|
|
97
87
|
};
|
|
98
88
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
89
|
+
async _summarizeSession(session) {
|
|
90
|
+
logger.info(`[ConversationMemory] Summarizing session ${session.sessionId}...`);
|
|
91
|
+
const targetTurns = this.config.summarizationTargetTurns || 10;
|
|
92
|
+
const splitIndex = Math.max(0, session.messages.length - targetTurns * MESSAGES_PER_TURN);
|
|
93
|
+
const messagesToSummarize = session.messages.slice(0, splitIndex);
|
|
94
|
+
const recentMessages = session.messages.slice(splitIndex);
|
|
95
|
+
if (messagesToSummarize.length === 0) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const summarizationPrompt = this._createSummarizationPrompt(messagesToSummarize);
|
|
99
|
+
const summarizer = new NeuroLink({ conversationMemory: { enabled: false } });
|
|
100
|
+
try {
|
|
101
|
+
const providerName = this.config.summarizationProvider;
|
|
102
|
+
// Map provider names to correct format
|
|
103
|
+
let mappedProvider = providerName;
|
|
104
|
+
if (providerName === 'vertex') {
|
|
105
|
+
mappedProvider = 'googlevertex';
|
|
106
|
+
}
|
|
107
|
+
if (!mappedProvider) {
|
|
108
|
+
logger.error(`[ConversationMemory] Missing summarization provider`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
logger.debug(`[ConversationMemory] Using provider: ${mappedProvider} for summarization`);
|
|
112
|
+
const summaryResult = await summarizer.generate({
|
|
113
|
+
input: { text: summarizationPrompt },
|
|
114
|
+
provider: mappedProvider,
|
|
115
|
+
model: this.config.summarizationModel,
|
|
116
|
+
disableTools: true,
|
|
117
|
+
});
|
|
118
|
+
if (summaryResult.content) {
|
|
119
|
+
session.messages = [
|
|
120
|
+
this.createSummarySystemMessage(summaryResult.content),
|
|
121
|
+
...recentMessages
|
|
122
|
+
];
|
|
123
|
+
logger.info(`[ConversationMemory] Summarization complete for session ${session.sessionId}.`);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
logger.warn(`[ConversationMemory] Summarization failed for session ${session.sessionId}. History not modified.`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
logger.error(`[ConversationMemory] Error during summarization for session ${session.sessionId}`, { error });
|
|
106
131
|
}
|
|
107
|
-
// Remove from memory
|
|
108
|
-
this.sessions.delete(sessionId);
|
|
109
|
-
logger.info("Session cleared", { sessionId });
|
|
110
|
-
return true;
|
|
111
132
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
133
|
+
_createSummarizationPrompt(history) {
|
|
134
|
+
const formattedHistory = history.map(msg => `${msg.role}: ${msg.content}`).join('\n\n');
|
|
135
|
+
return `
|
|
136
|
+
You are a context summarization AI. Your task is to condense the following conversation history for another AI assistant.
|
|
137
|
+
The summary must be a concise, third-person narrative that retains all critical information, including key entities, technical details, decisions made, and any specific dates or times mentioned.
|
|
138
|
+
Ensure the summary flows logically and is ready to be used as context for the next turn in the conversation.
|
|
139
|
+
|
|
140
|
+
Conversation History to Summarize:
|
|
141
|
+
---
|
|
142
|
+
${formattedHistory}
|
|
143
|
+
---
|
|
144
|
+
`.trim();
|
|
120
145
|
}
|
|
121
|
-
// Private methods
|
|
122
146
|
async ensureInitialized() {
|
|
123
147
|
if (!this.isInitialized) {
|
|
124
148
|
await this.initialize();
|
|
@@ -138,16 +162,33 @@ export class ConversationMemoryManager {
|
|
|
138
162
|
if (this.sessions.size <= maxSessions) {
|
|
139
163
|
return;
|
|
140
164
|
}
|
|
141
|
-
// Sort sessions by last activity (oldest first)
|
|
142
165
|
const sessions = Array.from(this.sessions.entries()).sort(([, a], [, b]) => a.lastActivity - b.lastActivity);
|
|
143
|
-
|
|
144
|
-
const sessionsToRemove = sessions.slice(0, sessions.length - maxSessions);
|
|
166
|
+
const sessionsToRemove = sessions.slice(0, this.sessions.size - maxSessions);
|
|
145
167
|
for (const [sessionId] of sessionsToRemove) {
|
|
146
168
|
this.sessions.delete(sessionId);
|
|
147
169
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
170
|
+
}
|
|
171
|
+
async getStats() {
|
|
172
|
+
await this.ensureInitialized();
|
|
173
|
+
const sessions = Array.from(this.sessions.values());
|
|
174
|
+
const totalTurns = sessions.reduce((sum, session) => sum + session.messages.length / MESSAGES_PER_TURN, 0);
|
|
175
|
+
return {
|
|
176
|
+
totalSessions: sessions.length,
|
|
177
|
+
totalTurns,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
async clearSession(sessionId) {
|
|
181
|
+
const session = this.sessions.get(sessionId);
|
|
182
|
+
if (!session) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
this.sessions.delete(sessionId);
|
|
186
|
+
logger.info("Session cleared", { sessionId });
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
async clearAllSessions() {
|
|
190
|
+
const sessionIds = Array.from(this.sessions.keys());
|
|
191
|
+
this.sessions.clear();
|
|
192
|
+
logger.info("All sessions cleared", { clearedCount: sessionIds.length });
|
|
152
193
|
}
|
|
153
194
|
}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { ZodUnknownSchema, ValidationSchema } from "../types/typeAliases.js
|
|
|
3
3
|
import type { GenerateResult } from "../types/generateTypes.js";
|
|
4
4
|
import type { StreamOptions, StreamResult } from "../types/streamTypes.js";
|
|
5
5
|
import type { JsonValue } from "../types/common.js";
|
|
6
|
-
import type { ChatMessage } from "../types/conversationTypes.js";
|
|
6
|
+
import type { ChatMessage, ConversationMemoryConfig } from "../types/conversationTypes.js";
|
|
7
7
|
import type { TokenUsage, AnalyticsData } from "../types/providers.js";
|
|
8
8
|
import type { EvaluationData } from "../index.js";
|
|
9
9
|
export type { EvaluationData };
|
|
@@ -155,6 +155,8 @@ export interface TextGenerationOptions {
|
|
|
155
155
|
content: string;
|
|
156
156
|
}>;
|
|
157
157
|
conversationMessages?: ChatMessage[];
|
|
158
|
+
conversationMemoryConfig?: Partial<ConversationMemoryConfig>;
|
|
159
|
+
originalPrompt?: string;
|
|
158
160
|
expectedOutcome?: string;
|
|
159
161
|
evaluationCriteria?: string[];
|
|
160
162
|
}
|
|
@@ -36,5 +36,10 @@ export function getConversationMemoryDefaults() {
|
|
|
36
36
|
maxSessions: Number(process.env.NEUROLINK_MEMORY_MAX_SESSIONS) || DEFAULT_MAX_SESSIONS,
|
|
37
37
|
maxTurnsPerSession: Number(process.env.NEUROLINK_MEMORY_MAX_TURNS_PER_SESSION) ||
|
|
38
38
|
DEFAULT_MAX_TURNS_PER_SESSION,
|
|
39
|
+
enableSummarization: process.env.NEUROLINK_SUMMARIZATION_ENABLED === "true",
|
|
40
|
+
summarizationThresholdTurns: Number(process.env.NEUROLINK_SUMMARIZATION_THRESHOLD_TURNS) || 20,
|
|
41
|
+
summarizationTargetTurns: Number(process.env.NEUROLINK_SUMMARIZATION_TARGET_TURNS) || 10,
|
|
42
|
+
summarizationProvider: process.env.NEUROLINK_SUMMARIZATION_PROVIDER || "vertex",
|
|
43
|
+
summarizationModel: process.env.NEUROLINK_SUMMARIZATION_MODEL || "gemini-2.5-flash",
|
|
39
44
|
};
|
|
40
45
|
}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Conversation Memory Manager for NeuroLink
|
|
3
3
|
* Handles in-memory conversation storage, session management, and context injection
|
|
4
4
|
*/
|
|
5
|
-
import type { ConversationMemoryConfig, ConversationMemoryStats, ChatMessage } from "../types/conversationTypes.js";
|
|
5
|
+
import type { ConversationMemoryConfig, SessionMemory, ConversationMemoryStats, ChatMessage } from "../types/conversationTypes.js";
|
|
6
6
|
export declare class ConversationMemoryManager {
|
|
7
7
|
private sessions;
|
|
8
|
-
|
|
8
|
+
config: ConversationMemoryConfig;
|
|
9
9
|
private isInitialized;
|
|
10
10
|
constructor(config: ConversationMemoryConfig);
|
|
11
11
|
/**
|
|
@@ -22,20 +22,14 @@ export declare class ConversationMemoryManager {
|
|
|
22
22
|
* Returns pre-stored message array with zero conversion overhead
|
|
23
23
|
*/
|
|
24
24
|
buildContextMessages(sessionId: string): ChatMessage[];
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
getStats(): Promise<ConversationMemoryStats>;
|
|
30
|
-
/**
|
|
31
|
-
* Clear all conversations for a specific session
|
|
32
|
-
*/
|
|
33
|
-
clearSession(sessionId: string): Promise<boolean>;
|
|
34
|
-
/**
|
|
35
|
-
* Clear all conversations (reset memory)
|
|
36
|
-
*/
|
|
37
|
-
clearAllSessions(): Promise<void>;
|
|
25
|
+
getSession(sessionId: string): SessionMemory | undefined;
|
|
26
|
+
createSummarySystemMessage(content: string): ChatMessage;
|
|
27
|
+
private _summarizeSession;
|
|
28
|
+
private _createSummarizationPrompt;
|
|
38
29
|
private ensureInitialized;
|
|
39
30
|
private createNewSession;
|
|
40
31
|
private enforceSessionLimit;
|
|
32
|
+
getStats(): Promise<ConversationMemoryStats>;
|
|
33
|
+
clearSession(sessionId: string): Promise<boolean>;
|
|
34
|
+
clearAllSessions(): Promise<void>;
|
|
41
35
|
}
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
* Handles in-memory conversation storage, session management, and context injection
|
|
4
4
|
*/
|
|
5
5
|
import { ConversationMemoryError } from "../types/conversationTypes.js";
|
|
6
|
-
import { DEFAULT_MAX_TURNS_PER_SESSION, DEFAULT_MAX_SESSIONS, MESSAGES_PER_TURN
|
|
6
|
+
import { DEFAULT_MAX_TURNS_PER_SESSION, DEFAULT_MAX_SESSIONS, MESSAGES_PER_TURN } from "../config/conversationMemoryConfig.js";
|
|
7
7
|
import { logger } from "../utils/logger.js";
|
|
8
|
+
import { NeuroLink } from "../neurolink.js";
|
|
8
9
|
export class ConversationMemoryManager {
|
|
9
10
|
sessions = new Map();
|
|
10
11
|
config;
|
|
11
12
|
isInitialized = false;
|
|
12
13
|
constructor(config) {
|
|
13
|
-
// Trust that config is already complete from applyConversationMemoryDefaults()
|
|
14
14
|
this.config = config;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
@@ -48,21 +48,19 @@ export class ConversationMemoryManager {
|
|
|
48
48
|
// ULTRA-OPTIMIZED: Direct message storage - no intermediate objects
|
|
49
49
|
session.messages.push({ role: "user", content: userMessage }, { role: "assistant", content: aiResponse });
|
|
50
50
|
session.lastActivity = Date.now();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
if (this.config.enableSummarization) {
|
|
52
|
+
const currentTurnCount = session.messages.length / MESSAGES_PER_TURN;
|
|
53
|
+
if (currentTurnCount > (this.config.summarizationThresholdTurns || 20)) {
|
|
54
|
+
await this._summarizeSession(session);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const maxMessages = (this.config.maxTurnsPerSession || DEFAULT_MAX_TURNS_PER_SESSION) * MESSAGES_PER_TURN;
|
|
59
|
+
if (session.messages.length > maxMessages) {
|
|
60
|
+
session.messages = session.messages.slice(-maxMessages);
|
|
61
|
+
}
|
|
56
62
|
}
|
|
57
|
-
// Enforce global session limit
|
|
58
63
|
this.enforceSessionLimit();
|
|
59
|
-
logger.debug("Conversation turn stored", {
|
|
60
|
-
sessionId,
|
|
61
|
-
messageCount: session.messages.length,
|
|
62
|
-
turnCount: session.messages.length / MESSAGES_PER_TURN, // Each turn = MESSAGES_PER_TURN messages
|
|
63
|
-
userMessageLength: userMessage.length,
|
|
64
|
-
aiResponseLength: aiResponse.length,
|
|
65
|
-
});
|
|
66
64
|
}
|
|
67
65
|
catch (error) {
|
|
68
66
|
throw new ConversationMemoryError(`Failed to store conversation turn for session ${sessionId}`, "STORAGE_ERROR", {
|
|
@@ -77,48 +75,74 @@ export class ConversationMemoryManager {
|
|
|
77
75
|
*/
|
|
78
76
|
buildContextMessages(sessionId) {
|
|
79
77
|
const session = this.sessions.get(sessionId);
|
|
80
|
-
|
|
81
|
-
return [];
|
|
82
|
-
}
|
|
83
|
-
// ULTRA-OPTIMIZED: Direct return - no processing needed!
|
|
84
|
-
return session.messages;
|
|
78
|
+
return session ? session.messages : [];
|
|
85
79
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
async getStats() {
|
|
91
|
-
await this.ensureInitialized();
|
|
92
|
-
const sessions = Array.from(this.sessions.values());
|
|
93
|
-
const totalTurns = sessions.reduce((sum, session) => sum + session.messages.length / MESSAGES_PER_TURN, 0);
|
|
80
|
+
getSession(sessionId) {
|
|
81
|
+
return this.sessions.get(sessionId);
|
|
82
|
+
}
|
|
83
|
+
createSummarySystemMessage(content) {
|
|
94
84
|
return {
|
|
95
|
-
|
|
96
|
-
|
|
85
|
+
role: "system",
|
|
86
|
+
content: `Summary of previous conversation turns:\n\n${content}`,
|
|
97
87
|
};
|
|
98
88
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
89
|
+
async _summarizeSession(session) {
|
|
90
|
+
logger.info(`[ConversationMemory] Summarizing session ${session.sessionId}...`);
|
|
91
|
+
const targetTurns = this.config.summarizationTargetTurns || 10;
|
|
92
|
+
const splitIndex = Math.max(0, session.messages.length - targetTurns * MESSAGES_PER_TURN);
|
|
93
|
+
const messagesToSummarize = session.messages.slice(0, splitIndex);
|
|
94
|
+
const recentMessages = session.messages.slice(splitIndex);
|
|
95
|
+
if (messagesToSummarize.length === 0) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const summarizationPrompt = this._createSummarizationPrompt(messagesToSummarize);
|
|
99
|
+
const summarizer = new NeuroLink({ conversationMemory: { enabled: false } });
|
|
100
|
+
try {
|
|
101
|
+
const providerName = this.config.summarizationProvider;
|
|
102
|
+
// Map provider names to correct format
|
|
103
|
+
let mappedProvider = providerName;
|
|
104
|
+
if (providerName === 'vertex') {
|
|
105
|
+
mappedProvider = 'googlevertex';
|
|
106
|
+
}
|
|
107
|
+
if (!mappedProvider) {
|
|
108
|
+
logger.error(`[ConversationMemory] Missing summarization provider`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
logger.debug(`[ConversationMemory] Using provider: ${mappedProvider} for summarization`);
|
|
112
|
+
const summaryResult = await summarizer.generate({
|
|
113
|
+
input: { text: summarizationPrompt },
|
|
114
|
+
provider: mappedProvider,
|
|
115
|
+
model: this.config.summarizationModel,
|
|
116
|
+
disableTools: true,
|
|
117
|
+
});
|
|
118
|
+
if (summaryResult.content) {
|
|
119
|
+
session.messages = [
|
|
120
|
+
this.createSummarySystemMessage(summaryResult.content),
|
|
121
|
+
...recentMessages
|
|
122
|
+
];
|
|
123
|
+
logger.info(`[ConversationMemory] Summarization complete for session ${session.sessionId}.`);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
logger.warn(`[ConversationMemory] Summarization failed for session ${session.sessionId}. History not modified.`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
logger.error(`[ConversationMemory] Error during summarization for session ${session.sessionId}`, { error });
|
|
106
131
|
}
|
|
107
|
-
// Remove from memory
|
|
108
|
-
this.sessions.delete(sessionId);
|
|
109
|
-
logger.info("Session cleared", { sessionId });
|
|
110
|
-
return true;
|
|
111
132
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
133
|
+
_createSummarizationPrompt(history) {
|
|
134
|
+
const formattedHistory = history.map(msg => `${msg.role}: ${msg.content}`).join('\n\n');
|
|
135
|
+
return `
|
|
136
|
+
You are a context summarization AI. Your task is to condense the following conversation history for another AI assistant.
|
|
137
|
+
The summary must be a concise, third-person narrative that retains all critical information, including key entities, technical details, decisions made, and any specific dates or times mentioned.
|
|
138
|
+
Ensure the summary flows logically and is ready to be used as context for the next turn in the conversation.
|
|
139
|
+
|
|
140
|
+
Conversation History to Summarize:
|
|
141
|
+
---
|
|
142
|
+
${formattedHistory}
|
|
143
|
+
---
|
|
144
|
+
`.trim();
|
|
120
145
|
}
|
|
121
|
-
// Private methods
|
|
122
146
|
async ensureInitialized() {
|
|
123
147
|
if (!this.isInitialized) {
|
|
124
148
|
await this.initialize();
|
|
@@ -138,16 +162,33 @@ export class ConversationMemoryManager {
|
|
|
138
162
|
if (this.sessions.size <= maxSessions) {
|
|
139
163
|
return;
|
|
140
164
|
}
|
|
141
|
-
// Sort sessions by last activity (oldest first)
|
|
142
165
|
const sessions = Array.from(this.sessions.entries()).sort(([, a], [, b]) => a.lastActivity - b.lastActivity);
|
|
143
|
-
|
|
144
|
-
const sessionsToRemove = sessions.slice(0, sessions.length - maxSessions);
|
|
166
|
+
const sessionsToRemove = sessions.slice(0, this.sessions.size - maxSessions);
|
|
145
167
|
for (const [sessionId] of sessionsToRemove) {
|
|
146
168
|
this.sessions.delete(sessionId);
|
|
147
169
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
170
|
+
}
|
|
171
|
+
async getStats() {
|
|
172
|
+
await this.ensureInitialized();
|
|
173
|
+
const sessions = Array.from(this.sessions.values());
|
|
174
|
+
const totalTurns = sessions.reduce((sum, session) => sum + session.messages.length / MESSAGES_PER_TURN, 0);
|
|
175
|
+
return {
|
|
176
|
+
totalSessions: sessions.length,
|
|
177
|
+
totalTurns,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
async clearSession(sessionId) {
|
|
181
|
+
const session = this.sessions.get(sessionId);
|
|
182
|
+
if (!session) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
this.sessions.delete(sessionId);
|
|
186
|
+
logger.info("Session cleared", { sessionId });
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
async clearAllSessions() {
|
|
190
|
+
const sessionIds = Array.from(this.sessions.keys());
|
|
191
|
+
this.sessions.clear();
|
|
192
|
+
logger.info("All sessions cleared", { clearedCount: sessionIds.length });
|
|
152
193
|
}
|
|
153
194
|
}
|
package/dist/lib/core/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { ZodUnknownSchema, ValidationSchema } from "../types/typeAliases.js
|
|
|
3
3
|
import type { GenerateResult } from "../types/generateTypes.js";
|
|
4
4
|
import type { StreamOptions, StreamResult } from "../types/streamTypes.js";
|
|
5
5
|
import type { JsonValue } from "../types/common.js";
|
|
6
|
-
import type { ChatMessage } from "../types/conversationTypes.js";
|
|
6
|
+
import type { ChatMessage, ConversationMemoryConfig } from "../types/conversationTypes.js";
|
|
7
7
|
import type { TokenUsage, AnalyticsData } from "../types/providers.js";
|
|
8
8
|
import type { EvaluationData } from "../index.js";
|
|
9
9
|
export type { EvaluationData };
|
|
@@ -155,6 +155,8 @@ export interface TextGenerationOptions {
|
|
|
155
155
|
content: string;
|
|
156
156
|
}>;
|
|
157
157
|
conversationMessages?: ChatMessage[];
|
|
158
|
+
conversationMemoryConfig?: Partial<ConversationMemoryConfig>;
|
|
159
|
+
originalPrompt?: string;
|
|
158
160
|
expectedOutcome?: string;
|
|
159
161
|
evaluationCriteria?: string[];
|
|
160
162
|
}
|
package/dist/lib/neurolink.d.ts
CHANGED
|
@@ -39,11 +39,9 @@ export interface MCPStatus {
|
|
|
39
39
|
error?: string;
|
|
40
40
|
[key: string]: unknown;
|
|
41
41
|
}
|
|
42
|
-
import type { ContextManagerConfig } from "./context/types.js";
|
|
43
42
|
export declare class NeuroLink {
|
|
44
43
|
private mcpInitialized;
|
|
45
44
|
private emitter;
|
|
46
|
-
private contextManager;
|
|
47
45
|
private autoDiscoveredServerInfos;
|
|
48
46
|
private externalServerManager;
|
|
49
47
|
private toolCircuitBreakers;
|
|
@@ -167,13 +165,6 @@ export declare class NeuroLink {
|
|
|
167
165
|
* @returns The original prompt text as a string.
|
|
168
166
|
*/
|
|
169
167
|
private _extractOriginalPrompt;
|
|
170
|
-
/**
|
|
171
|
-
* Enables automatic context summarization for the NeuroLink instance.
|
|
172
|
-
* Once enabled, the instance will maintain conversation history and
|
|
173
|
-
* automatically summarize it when it exceeds token limits.
|
|
174
|
-
* @param config Optional configuration to override default summarization settings.
|
|
175
|
-
*/
|
|
176
|
-
enableContextSummarization(config?: Partial<ContextManagerConfig>): void;
|
|
177
168
|
/**
|
|
178
169
|
* Generate AI content using the best available provider with MCP tool integration.
|
|
179
170
|
* This is the primary method for text generation with full feature support.
|