@hashgraphonline/conversational-agent 0.1.204 → 0.1.206
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/dist/cjs/base-agent.d.ts +6 -0
- package/dist/cjs/context/ReferenceContextManager.d.ts +84 -0
- package/dist/cjs/context/ReferenceResponseProcessor.d.ts +76 -0
- package/dist/cjs/conversational-agent.d.ts +34 -0
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/langchain-agent.d.ts +1 -0
- package/dist/cjs/mcp/ContentProcessor.d.ts +37 -0
- package/dist/cjs/mcp/MCPClientManager.d.ts +19 -1
- package/dist/cjs/memory/ContentStorage.d.ts +205 -0
- package/dist/cjs/memory/MemoryWindow.d.ts +114 -0
- package/dist/cjs/memory/ReferenceIdGenerator.d.ts +45 -0
- package/dist/cjs/memory/SmartMemoryManager.d.ts +201 -0
- package/dist/cjs/memory/TokenCounter.d.ts +61 -0
- package/dist/cjs/memory/index.d.ts +7 -0
- package/dist/cjs/plugins/hbar-transfer/TransferHbarTool.d.ts +18 -0
- package/dist/cjs/services/ContentStoreManager.d.ts +54 -0
- package/dist/cjs/types/content-reference.d.ts +213 -0
- package/dist/cjs/types/index.d.ts +4 -0
- package/dist/esm/index12.js +15 -2
- package/dist/esm/index12.js.map +1 -1
- package/dist/esm/index14.js +119 -95
- package/dist/esm/index14.js.map +1 -1
- package/dist/esm/index15.js +159 -114
- package/dist/esm/index15.js.map +1 -1
- package/dist/esm/index16.js +122 -81
- package/dist/esm/index16.js.map +1 -1
- package/dist/esm/index17.js +236 -0
- package/dist/esm/index17.js.map +1 -0
- package/dist/esm/index18.js +95 -0
- package/dist/esm/index18.js.map +1 -0
- package/dist/esm/index19.js +663 -0
- package/dist/esm/index19.js.map +1 -0
- package/dist/esm/index2.js +3 -1
- package/dist/esm/index2.js.map +1 -1
- package/dist/esm/index20.js +233 -0
- package/dist/esm/index20.js.map +1 -0
- package/dist/esm/index21.js +182 -0
- package/dist/esm/index21.js.map +1 -0
- package/dist/esm/index22.js +126 -0
- package/dist/esm/index22.js.map +1 -0
- package/dist/esm/index23.js +68 -0
- package/dist/esm/index23.js.map +1 -0
- package/dist/esm/index24.js +38 -0
- package/dist/esm/index24.js.map +1 -0
- package/dist/esm/index6.js +143 -84
- package/dist/esm/index6.js.map +1 -1
- package/dist/esm/index7.js.map +1 -1
- package/dist/esm/index8.js +69 -5
- package/dist/esm/index8.js.map +1 -1
- package/dist/types/base-agent.d.ts +6 -0
- package/dist/types/context/ReferenceContextManager.d.ts +84 -0
- package/dist/types/context/ReferenceResponseProcessor.d.ts +76 -0
- package/dist/types/conversational-agent.d.ts +34 -0
- package/dist/types/langchain-agent.d.ts +1 -0
- package/dist/types/mcp/ContentProcessor.d.ts +37 -0
- package/dist/types/mcp/MCPClientManager.d.ts +19 -1
- package/dist/types/memory/ContentStorage.d.ts +205 -0
- package/dist/types/memory/MemoryWindow.d.ts +114 -0
- package/dist/types/memory/ReferenceIdGenerator.d.ts +45 -0
- package/dist/types/memory/SmartMemoryManager.d.ts +201 -0
- package/dist/types/memory/TokenCounter.d.ts +61 -0
- package/dist/types/memory/index.d.ts +7 -0
- package/dist/types/plugins/hbar-transfer/TransferHbarTool.d.ts +18 -0
- package/dist/types/services/ContentStoreManager.d.ts +54 -0
- package/dist/types/types/content-reference.d.ts +213 -0
- package/dist/types/types/index.d.ts +4 -0
- package/package.json +30 -26
- package/src/base-agent.ts +6 -0
- package/src/context/ReferenceContextManager.ts +345 -0
- package/src/context/ReferenceResponseProcessor.ts +296 -0
- package/src/conversational-agent.ts +166 -92
- package/src/langchain-agent.ts +89 -2
- package/src/mcp/ContentProcessor.ts +317 -0
- package/src/mcp/MCPClientManager.ts +61 -1
- package/src/mcp/adapters/langchain.ts +9 -4
- package/src/memory/ContentStorage.ts +954 -0
- package/src/memory/MemoryWindow.ts +247 -0
- package/src/memory/ReferenceIdGenerator.ts +84 -0
- package/src/memory/SmartMemoryManager.ts +323 -0
- package/src/memory/TokenCounter.ts +152 -0
- package/src/memory/index.ts +8 -0
- package/src/plugins/hbar-transfer/TransferHbarTool.ts +19 -1
- package/src/plugins/hcs-10/HCS10Plugin.ts +5 -4
- package/src/services/ContentStoreManager.ts +199 -0
- package/src/types/content-reference.ts +281 -0
- package/src/types/index.ts +6 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
2
|
+
import { TokenCounter } from './TokenCounter';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Result of adding a message to the memory window
|
|
6
|
+
*/
|
|
7
|
+
export interface AddMessageResult {
|
|
8
|
+
/** Whether the message was successfully added */
|
|
9
|
+
added: boolean;
|
|
10
|
+
/** Messages that were pruned to make room */
|
|
11
|
+
prunedMessages: BaseMessage[];
|
|
12
|
+
/** Current token count after operation */
|
|
13
|
+
currentTokenCount: number;
|
|
14
|
+
/** Remaining token capacity */
|
|
15
|
+
remainingCapacity: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Memory window that manages conversation history with token-based size limits
|
|
20
|
+
* Automatically prunes old messages to stay within token limits while preserving conversational context
|
|
21
|
+
*/
|
|
22
|
+
export class MemoryWindow {
|
|
23
|
+
private messages: BaseMessage[] = [];
|
|
24
|
+
private maxTokens: number;
|
|
25
|
+
private reserveTokens: number;
|
|
26
|
+
private tokenCounter: TokenCounter;
|
|
27
|
+
private systemPrompt: string = '';
|
|
28
|
+
private systemPromptTokens: number = 0;
|
|
29
|
+
|
|
30
|
+
// Default token limits for different model contexts
|
|
31
|
+
public static readonly DEFAULT_MAX_TOKENS = 8000; // Conservative limit for most models
|
|
32
|
+
public static readonly DEFAULT_RESERVE_TOKENS = 1000; // Reserve for response generation
|
|
33
|
+
public static readonly PRUNING_BATCH_SIZE = 2; // Remove messages in pairs to maintain conversation flow
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
maxTokens: number = MemoryWindow.DEFAULT_MAX_TOKENS,
|
|
37
|
+
reserveTokens: number = MemoryWindow.DEFAULT_RESERVE_TOKENS,
|
|
38
|
+
tokenCounter?: TokenCounter
|
|
39
|
+
) {
|
|
40
|
+
if (reserveTokens >= maxTokens) {
|
|
41
|
+
throw new Error('Reserve tokens must be less than max tokens');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.maxTokens = maxTokens;
|
|
45
|
+
this.reserveTokens = reserveTokens;
|
|
46
|
+
this.tokenCounter = tokenCounter || new TokenCounter();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Add a message to the memory window, pruning old messages if necessary
|
|
51
|
+
* @param message - The message to add
|
|
52
|
+
* @returns Result of the add operation including any pruned messages
|
|
53
|
+
*/
|
|
54
|
+
addMessage(message: BaseMessage): AddMessageResult {
|
|
55
|
+
// Calculate tokens for the new message
|
|
56
|
+
const messageTokens = this.tokenCounter.countMessageTokens(message);
|
|
57
|
+
|
|
58
|
+
// Add the message first
|
|
59
|
+
this.messages.push(message);
|
|
60
|
+
|
|
61
|
+
// Check if we need to prune
|
|
62
|
+
const currentTokens = this.getCurrentTokenCount();
|
|
63
|
+
const availableTokens = this.maxTokens - this.reserveTokens;
|
|
64
|
+
|
|
65
|
+
let prunedMessages: BaseMessage[] = [];
|
|
66
|
+
|
|
67
|
+
if (currentTokens > availableTokens) {
|
|
68
|
+
// Need to prune - remove the new message temporarily
|
|
69
|
+
this.messages.pop();
|
|
70
|
+
|
|
71
|
+
// Prune old messages to make room
|
|
72
|
+
prunedMessages = this.pruneToFit();
|
|
73
|
+
|
|
74
|
+
// Add the new message back
|
|
75
|
+
this.messages.push(message);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
added: true,
|
|
80
|
+
prunedMessages,
|
|
81
|
+
currentTokenCount: this.getCurrentTokenCount(),
|
|
82
|
+
remainingCapacity: this.getRemainingTokenCapacity()
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Prune old messages to fit within token limits
|
|
88
|
+
* Removes messages in pairs to maintain conversational flow
|
|
89
|
+
* @returns Array of pruned messages
|
|
90
|
+
*/
|
|
91
|
+
pruneToFit(): BaseMessage[] {
|
|
92
|
+
const prunedMessages: BaseMessage[] = [];
|
|
93
|
+
const targetTokens = this.maxTokens - this.reserveTokens;
|
|
94
|
+
|
|
95
|
+
while (this.getCurrentTokenCount() > targetTokens && this.messages.length > 0) {
|
|
96
|
+
// Remove messages in batches to maintain conversation flow
|
|
97
|
+
const batchSize = Math.min(MemoryWindow.PRUNING_BATCH_SIZE, this.messages.length);
|
|
98
|
+
|
|
99
|
+
for (let i = 0; i < batchSize; i++) {
|
|
100
|
+
const prunedMessage = this.messages.shift();
|
|
101
|
+
if (prunedMessage) {
|
|
102
|
+
prunedMessages.push(prunedMessage);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Safety check to prevent infinite loop
|
|
107
|
+
if (prunedMessages.length > 1000) {
|
|
108
|
+
console.warn('MemoryWindow: Excessive pruning detected, stopping to prevent infinite loop');
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return prunedMessages;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get current token count including system prompt and messages
|
|
118
|
+
* @returns Current token count
|
|
119
|
+
*/
|
|
120
|
+
getCurrentTokenCount(): number {
|
|
121
|
+
const messageTokens = this.tokenCounter.countMessagesTokens(this.messages);
|
|
122
|
+
return this.systemPromptTokens + messageTokens;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get remaining token capacity before hitting the reserve limit
|
|
127
|
+
* @returns Remaining tokens that can be used
|
|
128
|
+
*/
|
|
129
|
+
getRemainingTokenCapacity(): number {
|
|
130
|
+
return Math.max(0, this.maxTokens - this.getCurrentTokenCount());
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Check if a message can be added without exceeding limits
|
|
135
|
+
* @param message - The message to check
|
|
136
|
+
* @returns True if message can be added within reserve limits
|
|
137
|
+
*/
|
|
138
|
+
canAddMessage(message: BaseMessage): boolean {
|
|
139
|
+
const messageTokens = this.tokenCounter.countMessageTokens(message);
|
|
140
|
+
const currentTokens = this.getCurrentTokenCount();
|
|
141
|
+
const wouldExceedReserve = (currentTokens + messageTokens) > (this.maxTokens - this.reserveTokens);
|
|
142
|
+
|
|
143
|
+
// Always allow adding if we can prune to make room
|
|
144
|
+
// Only return false if the message itself is larger than our total capacity
|
|
145
|
+
if (messageTokens > this.maxTokens) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return !wouldExceedReserve || this.messages.length > 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get all messages in the memory window
|
|
154
|
+
* @returns Copy of current messages array
|
|
155
|
+
*/
|
|
156
|
+
getMessages(): BaseMessage[] {
|
|
157
|
+
return [...this.messages];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Clear all messages from the memory window
|
|
162
|
+
*/
|
|
163
|
+
clear(): void {
|
|
164
|
+
this.messages = [];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Set the system prompt and update token calculations
|
|
169
|
+
* @param systemPrompt - The system prompt text
|
|
170
|
+
*/
|
|
171
|
+
setSystemPrompt(systemPrompt: string): void {
|
|
172
|
+
this.systemPrompt = systemPrompt;
|
|
173
|
+
this.systemPromptTokens = this.tokenCounter.estimateSystemPromptTokens(systemPrompt);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get the current system prompt
|
|
178
|
+
* @returns Current system prompt
|
|
179
|
+
*/
|
|
180
|
+
getSystemPrompt(): string {
|
|
181
|
+
return this.systemPrompt;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get current configuration
|
|
186
|
+
* @returns Memory window configuration
|
|
187
|
+
*/
|
|
188
|
+
getConfig() {
|
|
189
|
+
return {
|
|
190
|
+
maxTokens: this.maxTokens,
|
|
191
|
+
reserveTokens: this.reserveTokens,
|
|
192
|
+
currentTokens: this.getCurrentTokenCount(),
|
|
193
|
+
messageCount: this.messages.length,
|
|
194
|
+
systemPromptTokens: this.systemPromptTokens
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Update token limits
|
|
200
|
+
* @param maxTokens - New maximum token limit
|
|
201
|
+
* @param reserveTokens - New reserve token amount
|
|
202
|
+
*/
|
|
203
|
+
updateLimits(maxTokens: number, reserveTokens?: number): void {
|
|
204
|
+
if (reserveTokens !== undefined && reserveTokens >= maxTokens) {
|
|
205
|
+
throw new Error('Reserve tokens must be less than max tokens');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
this.maxTokens = maxTokens;
|
|
209
|
+
if (reserveTokens !== undefined) {
|
|
210
|
+
this.reserveTokens = reserveTokens;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Prune if necessary after updating limits
|
|
214
|
+
if (this.getCurrentTokenCount() > (this.maxTokens - this.reserveTokens)) {
|
|
215
|
+
this.pruneToFit();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get statistics about the memory window
|
|
221
|
+
* @returns Memory usage statistics
|
|
222
|
+
*/
|
|
223
|
+
getStats() {
|
|
224
|
+
const currentTokens = this.getCurrentTokenCount();
|
|
225
|
+
const capacity = this.maxTokens;
|
|
226
|
+
const usagePercentage = (currentTokens / capacity) * 100;
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
totalMessages: this.messages.length,
|
|
230
|
+
currentTokens,
|
|
231
|
+
maxTokens: capacity,
|
|
232
|
+
reserveTokens: this.reserveTokens,
|
|
233
|
+
systemPromptTokens: this.systemPromptTokens,
|
|
234
|
+
usagePercentage: Math.round(usagePercentage * 100) / 100,
|
|
235
|
+
remainingCapacity: this.getRemainingTokenCapacity(),
|
|
236
|
+
canAcceptMore: this.getRemainingTokenCapacity() > this.reserveTokens
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Clean up resources
|
|
242
|
+
*/
|
|
243
|
+
dispose(): void {
|
|
244
|
+
this.clear();
|
|
245
|
+
this.tokenCounter.dispose();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import { ReferenceId } from '../types/content-reference';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Content-based reference ID generator using SHA-256 (HCS-1 style)
|
|
6
|
+
*
|
|
7
|
+
* Generates deterministic reference IDs based on content hashing.
|
|
8
|
+
* Same content always produces the same reference ID.
|
|
9
|
+
*/
|
|
10
|
+
export class ReferenceIdGenerator {
|
|
11
|
+
/**
|
|
12
|
+
* Generate a content-based reference ID using SHA-256 hashing
|
|
13
|
+
*
|
|
14
|
+
* @param content The content to generate a reference ID for
|
|
15
|
+
* @returns Deterministic reference ID based on content hash
|
|
16
|
+
*/
|
|
17
|
+
static generateId(content: Buffer): ReferenceId {
|
|
18
|
+
const hash = createHash('sha256');
|
|
19
|
+
hash.update(content);
|
|
20
|
+
return hash.digest('base64url');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Validate that a string is a properly formatted reference ID
|
|
25
|
+
*
|
|
26
|
+
* @param id The ID to validate
|
|
27
|
+
* @returns true if the ID is valid format
|
|
28
|
+
*/
|
|
29
|
+
static isValidReferenceId(id: string): id is ReferenceId {
|
|
30
|
+
if (!id || typeof id !== 'string') {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check length (base64url encoding of SHA-256 hash = 43 chars)
|
|
35
|
+
if (id.length !== 43) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check character set (base64url: A-Z, a-z, 0-9, -, _)
|
|
40
|
+
return /^[A-Za-z0-9_-]+$/.test(id);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Extract reference ID from ref:// format
|
|
45
|
+
*
|
|
46
|
+
* @param input Input string that may contain a reference ID
|
|
47
|
+
* @returns Extracted reference ID or null if not found
|
|
48
|
+
*/
|
|
49
|
+
static extractReferenceId(input: string): ReferenceId | null {
|
|
50
|
+
if (!input || typeof input !== 'string') {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check for ref:// format
|
|
55
|
+
const refFormatMatch = input.match(/^ref:\/\/([A-Za-z0-9_-]{43})$/);
|
|
56
|
+
if (refFormatMatch) {
|
|
57
|
+
return refFormatMatch[1] as ReferenceId;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check if input is directly a valid reference ID
|
|
61
|
+
return this.isValidReferenceId(input) ? input : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Format a reference ID in the standard ref:// format
|
|
66
|
+
*
|
|
67
|
+
* @param referenceId The reference ID to format
|
|
68
|
+
* @returns Formatted reference string
|
|
69
|
+
*/
|
|
70
|
+
static formatReference(referenceId: ReferenceId): string {
|
|
71
|
+
return `ref://${referenceId}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generate a test reference ID (for testing purposes only)
|
|
76
|
+
*
|
|
77
|
+
* @param testSeed A test seed to generate a fake but valid ID format
|
|
78
|
+
* @returns A valid format reference ID for testing
|
|
79
|
+
*/
|
|
80
|
+
static generateTestId(testSeed: string): ReferenceId {
|
|
81
|
+
const content = Buffer.from(`test-${testSeed}-${Date.now()}`);
|
|
82
|
+
return this.generateId(content);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
2
|
+
import { MemoryWindow } from './MemoryWindow';
|
|
3
|
+
import { ContentStorage } from './ContentStorage';
|
|
4
|
+
import { TokenCounter } from './TokenCounter';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for SmartMemoryManager
|
|
8
|
+
*/
|
|
9
|
+
export interface SmartMemoryConfig {
|
|
10
|
+
/** Maximum tokens for active memory window */
|
|
11
|
+
maxTokens?: number;
|
|
12
|
+
/** Reserve tokens for response generation */
|
|
13
|
+
reserveTokens?: number;
|
|
14
|
+
/** Model name for token counting */
|
|
15
|
+
modelName?: string;
|
|
16
|
+
/** Maximum messages to store in content storage */
|
|
17
|
+
storageLimit?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Search options for history search
|
|
22
|
+
*/
|
|
23
|
+
export interface SearchOptions {
|
|
24
|
+
/** Whether to perform case-sensitive search */
|
|
25
|
+
caseSensitive?: boolean;
|
|
26
|
+
/** Maximum number of results to return */
|
|
27
|
+
limit?: number;
|
|
28
|
+
/** Whether to use regex pattern matching */
|
|
29
|
+
useRegex?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Memory statistics for active memory window
|
|
34
|
+
*/
|
|
35
|
+
export interface MemoryStats {
|
|
36
|
+
/** Total active messages in memory window */
|
|
37
|
+
totalActiveMessages: number;
|
|
38
|
+
/** Current token count including system prompt */
|
|
39
|
+
currentTokenCount: number;
|
|
40
|
+
/** Maximum token capacity */
|
|
41
|
+
maxTokens: number;
|
|
42
|
+
/** Remaining token capacity */
|
|
43
|
+
remainingCapacity: number;
|
|
44
|
+
/** System prompt token count */
|
|
45
|
+
systemPromptTokens: number;
|
|
46
|
+
/** Memory usage percentage */
|
|
47
|
+
usagePercentage: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Smart memory manager that combines active memory window with long-term storage
|
|
52
|
+
* Provides context-aware memory management with automatic pruning and searchable history
|
|
53
|
+
*/
|
|
54
|
+
export class SmartMemoryManager {
|
|
55
|
+
private memoryWindow: MemoryWindow;
|
|
56
|
+
private contentStorage: ContentStorage;
|
|
57
|
+
private tokenCounter: TokenCounter;
|
|
58
|
+
private config: Required<SmartMemoryConfig>;
|
|
59
|
+
|
|
60
|
+
// Default configuration values
|
|
61
|
+
private static readonly DEFAULT_CONFIG: Required<SmartMemoryConfig> = {
|
|
62
|
+
maxTokens: 8000,
|
|
63
|
+
reserveTokens: 1000,
|
|
64
|
+
modelName: 'gpt-4o',
|
|
65
|
+
storageLimit: 1000
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
constructor(config: SmartMemoryConfig = {}) {
|
|
69
|
+
this.config = { ...SmartMemoryManager.DEFAULT_CONFIG, ...config };
|
|
70
|
+
|
|
71
|
+
// Initialize components
|
|
72
|
+
this.tokenCounter = new TokenCounter(this.config.modelName as any);
|
|
73
|
+
this.contentStorage = new ContentStorage(this.config.storageLimit);
|
|
74
|
+
this.memoryWindow = new MemoryWindow(
|
|
75
|
+
this.config.maxTokens,
|
|
76
|
+
this.config.reserveTokens,
|
|
77
|
+
this.tokenCounter
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Add a message to the active memory window
|
|
83
|
+
* Automatically handles pruning and storage of displaced messages
|
|
84
|
+
* @param message - Message to add
|
|
85
|
+
*/
|
|
86
|
+
addMessage(message: BaseMessage): void {
|
|
87
|
+
const result = this.memoryWindow.addMessage(message);
|
|
88
|
+
|
|
89
|
+
// Store any pruned messages in content storage
|
|
90
|
+
if (result.prunedMessages.length > 0) {
|
|
91
|
+
this.contentStorage.storeMessages(result.prunedMessages);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get all active messages from the memory window
|
|
97
|
+
* @returns Array of active messages in chronological order
|
|
98
|
+
*/
|
|
99
|
+
getMessages(): BaseMessage[] {
|
|
100
|
+
return this.memoryWindow.getMessages();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Clear active memory window
|
|
105
|
+
* @param clearStorage - Whether to also clear the content storage (default: false)
|
|
106
|
+
*/
|
|
107
|
+
clear(clearStorage: boolean = false): void {
|
|
108
|
+
this.memoryWindow.clear();
|
|
109
|
+
|
|
110
|
+
if (clearStorage) {
|
|
111
|
+
this.contentStorage.clear();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Set the system prompt for the memory window
|
|
117
|
+
* @param systemPrompt - System prompt text
|
|
118
|
+
*/
|
|
119
|
+
setSystemPrompt(systemPrompt: string): void {
|
|
120
|
+
this.memoryWindow.setSystemPrompt(systemPrompt);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get the current system prompt
|
|
125
|
+
* @returns Current system prompt text
|
|
126
|
+
*/
|
|
127
|
+
getSystemPrompt(): string {
|
|
128
|
+
return this.memoryWindow.getSystemPrompt();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Search through stored message history
|
|
133
|
+
* @param query - Search term or pattern
|
|
134
|
+
* @param options - Search configuration
|
|
135
|
+
* @returns Array of matching messages from history
|
|
136
|
+
*/
|
|
137
|
+
searchHistory(query: string, options: SearchOptions = {}): BaseMessage[] {
|
|
138
|
+
return this.contentStorage.searchMessages(query, options);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get recent messages from storage history
|
|
143
|
+
* @param count - Number of recent messages to retrieve
|
|
144
|
+
* @returns Array of recent messages from storage
|
|
145
|
+
*/
|
|
146
|
+
getRecentHistory(count: number): BaseMessage[] {
|
|
147
|
+
return this.contentStorage.getRecentMessages(count);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Check if a message can be added without exceeding limits
|
|
152
|
+
* @param message - Message to test
|
|
153
|
+
* @returns True if message can be added
|
|
154
|
+
*/
|
|
155
|
+
canAddMessage(message: BaseMessage): boolean {
|
|
156
|
+
return this.memoryWindow.canAddMessage(message);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get statistics about the active memory window
|
|
161
|
+
* @returns Memory usage statistics
|
|
162
|
+
*/
|
|
163
|
+
getMemoryStats(): MemoryStats {
|
|
164
|
+
const windowStats = this.memoryWindow.getStats();
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
totalActiveMessages: windowStats.totalMessages,
|
|
168
|
+
currentTokenCount: windowStats.currentTokens,
|
|
169
|
+
maxTokens: windowStats.maxTokens,
|
|
170
|
+
remainingCapacity: windowStats.remainingCapacity,
|
|
171
|
+
systemPromptTokens: windowStats.systemPromptTokens,
|
|
172
|
+
usagePercentage: windowStats.usagePercentage
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get statistics about the content storage
|
|
178
|
+
* @returns Storage usage statistics
|
|
179
|
+
*/
|
|
180
|
+
getStorageStats() {
|
|
181
|
+
return this.contentStorage.getStorageStats();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get combined statistics for both active memory and storage
|
|
186
|
+
* @returns Combined memory and storage statistics
|
|
187
|
+
*/
|
|
188
|
+
getOverallStats() {
|
|
189
|
+
const memoryStats = this.getMemoryStats();
|
|
190
|
+
const storageStats = this.getStorageStats();
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
activeMemory: memoryStats,
|
|
194
|
+
storage: storageStats,
|
|
195
|
+
totalMessagesManaged: memoryStats.totalActiveMessages + storageStats.totalMessages,
|
|
196
|
+
activeMemoryUtilization: memoryStats.usagePercentage,
|
|
197
|
+
storageUtilization: storageStats.usagePercentage
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Update the configuration and apply changes
|
|
203
|
+
* @param newConfig - New configuration options
|
|
204
|
+
*/
|
|
205
|
+
updateConfig(newConfig: Partial<SmartMemoryConfig>): void {
|
|
206
|
+
this.config = { ...this.config, ...newConfig };
|
|
207
|
+
|
|
208
|
+
// Update components with new configuration
|
|
209
|
+
if (newConfig.maxTokens !== undefined || newConfig.reserveTokens !== undefined) {
|
|
210
|
+
this.memoryWindow.updateLimits(
|
|
211
|
+
this.config.maxTokens,
|
|
212
|
+
this.config.reserveTokens
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (newConfig.storageLimit !== undefined) {
|
|
217
|
+
this.contentStorage.updateStorageLimit(this.config.storageLimit);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Note: Model name changes would require recreating the token counter
|
|
221
|
+
// This is not implemented to avoid disrupting ongoing operations
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Get current configuration
|
|
226
|
+
* @returns Current configuration settings
|
|
227
|
+
*/
|
|
228
|
+
getConfig(): Required<SmartMemoryConfig> {
|
|
229
|
+
return { ...this.config };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Get messages from storage within a time range
|
|
234
|
+
* @param startTime - Start of time range
|
|
235
|
+
* @param endTime - End of time range
|
|
236
|
+
* @returns Messages within the specified time range
|
|
237
|
+
*/
|
|
238
|
+
getHistoryFromTimeRange(startTime: Date, endTime: Date): BaseMessage[] {
|
|
239
|
+
return this.contentStorage.getMessagesFromTimeRange(startTime, endTime);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Get messages from storage by message type
|
|
244
|
+
* @param messageType - Type of messages to retrieve ('human', 'ai', 'system', etc.)
|
|
245
|
+
* @param limit - Maximum number of messages to return
|
|
246
|
+
* @returns Messages of the specified type
|
|
247
|
+
*/
|
|
248
|
+
getHistoryByType(messageType: string, limit?: number): BaseMessage[] {
|
|
249
|
+
return this.contentStorage.getMessagesByType(messageType, limit);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get recent messages from storage within the last N minutes
|
|
254
|
+
* @param minutes - Number of minutes to look back
|
|
255
|
+
* @returns Messages from the last N minutes
|
|
256
|
+
*/
|
|
257
|
+
getRecentHistoryByTime(minutes: number): BaseMessage[] {
|
|
258
|
+
return this.contentStorage.getRecentMessagesByTime(minutes);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Export the current state for persistence or analysis
|
|
263
|
+
* @returns Serializable representation of memory state
|
|
264
|
+
*/
|
|
265
|
+
exportState() {
|
|
266
|
+
return {
|
|
267
|
+
config: this.config,
|
|
268
|
+
activeMessages: this.memoryWindow.getMessages().map(msg => ({
|
|
269
|
+
content: msg.content,
|
|
270
|
+
type: msg._getType()
|
|
271
|
+
})),
|
|
272
|
+
systemPrompt: this.memoryWindow.getSystemPrompt(),
|
|
273
|
+
memoryStats: this.getMemoryStats(),
|
|
274
|
+
storageStats: this.getStorageStats(),
|
|
275
|
+
storedMessages: this.contentStorage.exportMessages()
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Get a summary of conversation context for external use
|
|
281
|
+
* Useful for providing context to other systems or for logging
|
|
282
|
+
* @param includeStoredContext - Whether to include recent stored messages
|
|
283
|
+
* @returns Context summary object
|
|
284
|
+
*/
|
|
285
|
+
getContextSummary(includeStoredContext: boolean = false) {
|
|
286
|
+
const activeMessages = this.getMessages();
|
|
287
|
+
const summary = {
|
|
288
|
+
activeMessageCount: activeMessages.length,
|
|
289
|
+
systemPrompt: this.getSystemPrompt(),
|
|
290
|
+
recentMessages: activeMessages.slice(-5), // Last 5 active messages
|
|
291
|
+
memoryUtilization: this.getMemoryStats().usagePercentage,
|
|
292
|
+
hasStoredHistory: this.getStorageStats().totalMessages > 0
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
if (includeStoredContext) {
|
|
296
|
+
return {
|
|
297
|
+
...summary,
|
|
298
|
+
recentStoredMessages: this.getRecentHistory(10), // Last 10 stored messages
|
|
299
|
+
storageStats: this.getStorageStats()
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return summary;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Perform maintenance operations
|
|
308
|
+
* Optimizes storage and cleans up resources
|
|
309
|
+
*/
|
|
310
|
+
performMaintenance(): void {
|
|
311
|
+
// No specific maintenance needed currently
|
|
312
|
+
// This method is reserved for future optimizations
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Clean up resources and dispose of components
|
|
317
|
+
*/
|
|
318
|
+
dispose(): void {
|
|
319
|
+
this.memoryWindow.dispose();
|
|
320
|
+
this.contentStorage.dispose();
|
|
321
|
+
this.tokenCounter.dispose();
|
|
322
|
+
}
|
|
323
|
+
}
|