@lanonasis/cli 3.0.1 → 3.0.2
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/commands/enhanced-memory.d.ts +5 -0
- package/dist/commands/enhanced-memory.js +404 -0
- package/dist/commands/mcp.js +27 -6
- package/dist/index.js +0 -0
- package/dist/mcp/access-control.d.ts +68 -0
- package/dist/mcp/access-control.js +228 -0
- package/dist/mcp/enhanced-server.d.ts +38 -0
- package/dist/mcp/enhanced-server.js +320 -0
- package/dist/mcp/logger.d.ts +20 -0
- package/dist/mcp/logger.js +47 -0
- package/dist/mcp/memory-state.d.ts +81 -0
- package/dist/mcp/memory-state.js +301 -0
- package/dist/mcp/schemas/tool-schemas.d.ts +8 -8
- package/dist/mcp/vector-store.d.ts +31 -0
- package/dist/mcp/vector-store.js +92 -0
- package/package.json +1 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory State Management System
|
|
3
|
+
* Implements comprehensive memory lifecycle management inspired by mem0's state system
|
|
4
|
+
*/
|
|
5
|
+
export declare enum MemoryState {
|
|
6
|
+
ACTIVE = "active",
|
|
7
|
+
PAUSED = "paused",
|
|
8
|
+
ARCHIVED = "archived",
|
|
9
|
+
DELETED = "deleted"
|
|
10
|
+
}
|
|
11
|
+
export interface MemoryStateTransition {
|
|
12
|
+
id: string;
|
|
13
|
+
memory_id: string;
|
|
14
|
+
from_state: MemoryState;
|
|
15
|
+
to_state: MemoryState;
|
|
16
|
+
reason?: string;
|
|
17
|
+
metadata?: Record<string, any>;
|
|
18
|
+
timestamp: string;
|
|
19
|
+
user_id: string;
|
|
20
|
+
}
|
|
21
|
+
export interface LanonasisMemory {
|
|
22
|
+
id: string;
|
|
23
|
+
user_id: string;
|
|
24
|
+
app_id: string;
|
|
25
|
+
title: string;
|
|
26
|
+
content: string;
|
|
27
|
+
state: MemoryState;
|
|
28
|
+
metadata: Record<string, any>;
|
|
29
|
+
categories: string[];
|
|
30
|
+
created_at: string;
|
|
31
|
+
updated_at: string;
|
|
32
|
+
archived_at?: string;
|
|
33
|
+
deleted_at?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface BulkOperationResult {
|
|
36
|
+
memory_id: string;
|
|
37
|
+
success: boolean;
|
|
38
|
+
previous_state: MemoryState;
|
|
39
|
+
new_state: MemoryState;
|
|
40
|
+
error?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare class MemoryStateManager {
|
|
43
|
+
private config;
|
|
44
|
+
private stateTransitions;
|
|
45
|
+
constructor();
|
|
46
|
+
initialize(): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Update memory state with validation and history tracking
|
|
49
|
+
*/
|
|
50
|
+
updateMemoryState(memoryId: string, newState: MemoryState, reason?: string, metadata?: Record<string, any>): Promise<MemoryStateTransition>;
|
|
51
|
+
/**
|
|
52
|
+
* Bulk state update operations
|
|
53
|
+
*/
|
|
54
|
+
bulkUpdateState(memoryIds: string[], operation: 'pause' | 'delete' | 'archive'): Promise<BulkOperationResult[]>;
|
|
55
|
+
/**
|
|
56
|
+
* Get memory state history
|
|
57
|
+
*/
|
|
58
|
+
getMemoryStateHistory(memoryId: string): MemoryStateTransition[];
|
|
59
|
+
/**
|
|
60
|
+
* Get memories by state
|
|
61
|
+
*/
|
|
62
|
+
getMemoriesByState(state: MemoryState, userId?: string, appId?: string, limit?: number): Promise<LanonasisMemory[]>;
|
|
63
|
+
/**
|
|
64
|
+
* Archive old memories based on policy
|
|
65
|
+
*/
|
|
66
|
+
archiveOldMemories(beforeDate: string, userId?: string, appId?: string): Promise<BulkOperationResult[]>;
|
|
67
|
+
/**
|
|
68
|
+
* Restore memories from archived/paused state
|
|
69
|
+
*/
|
|
70
|
+
restoreMemories(memoryIds: string[]): Promise<BulkOperationResult[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Private helper methods
|
|
73
|
+
*/
|
|
74
|
+
private isValidTransition;
|
|
75
|
+
private operationToState;
|
|
76
|
+
private getMemory;
|
|
77
|
+
private updateMemoryViaAPI;
|
|
78
|
+
private callMemoryAPI;
|
|
79
|
+
private getCurrentUserId;
|
|
80
|
+
private generateTransitionId;
|
|
81
|
+
}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory State Management System
|
|
3
|
+
* Implements comprehensive memory lifecycle management inspired by mem0's state system
|
|
4
|
+
*/
|
|
5
|
+
import { CLIConfig } from '../utils/config.js';
|
|
6
|
+
import { logger } from './logger.js';
|
|
7
|
+
export var MemoryState;
|
|
8
|
+
(function (MemoryState) {
|
|
9
|
+
MemoryState["ACTIVE"] = "active";
|
|
10
|
+
MemoryState["PAUSED"] = "paused";
|
|
11
|
+
MemoryState["ARCHIVED"] = "archived";
|
|
12
|
+
MemoryState["DELETED"] = "deleted";
|
|
13
|
+
})(MemoryState || (MemoryState = {}));
|
|
14
|
+
export class MemoryStateManager {
|
|
15
|
+
config;
|
|
16
|
+
stateTransitions = [];
|
|
17
|
+
constructor() {
|
|
18
|
+
this.config = new CLIConfig();
|
|
19
|
+
}
|
|
20
|
+
async initialize() {
|
|
21
|
+
logger.info('Memory State Manager initialized');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Update memory state with validation and history tracking
|
|
25
|
+
*/
|
|
26
|
+
async updateMemoryState(memoryId, newState, reason, metadata) {
|
|
27
|
+
try {
|
|
28
|
+
// Get current memory state
|
|
29
|
+
const memory = await this.getMemory(memoryId);
|
|
30
|
+
if (!memory) {
|
|
31
|
+
throw new Error(`Memory ${memoryId} not found`);
|
|
32
|
+
}
|
|
33
|
+
const currentState = memory.state;
|
|
34
|
+
// Validate state transition
|
|
35
|
+
if (!this.isValidTransition(currentState, newState)) {
|
|
36
|
+
throw new Error(`Invalid state transition from ${currentState} to ${newState}`);
|
|
37
|
+
}
|
|
38
|
+
// Create state transition record
|
|
39
|
+
const transition = {
|
|
40
|
+
id: this.generateTransitionId(),
|
|
41
|
+
memory_id: memoryId,
|
|
42
|
+
from_state: currentState,
|
|
43
|
+
to_state: newState,
|
|
44
|
+
reason,
|
|
45
|
+
metadata,
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
user_id: await this.getCurrentUserId()
|
|
48
|
+
};
|
|
49
|
+
// Update memory state via API
|
|
50
|
+
await this.updateMemoryViaAPI(memoryId, {
|
|
51
|
+
state: newState,
|
|
52
|
+
updated_at: transition.timestamp,
|
|
53
|
+
archived_at: newState === MemoryState.ARCHIVED
|
|
54
|
+
? transition.timestamp
|
|
55
|
+
: null,
|
|
56
|
+
deleted_at: newState === MemoryState.DELETED
|
|
57
|
+
? transition.timestamp
|
|
58
|
+
: null
|
|
59
|
+
});
|
|
60
|
+
// Record transition
|
|
61
|
+
this.stateTransitions.push(transition);
|
|
62
|
+
logger.info('Memory state updated', {
|
|
63
|
+
memoryId,
|
|
64
|
+
fromState: currentState,
|
|
65
|
+
toState: newState,
|
|
66
|
+
reason
|
|
67
|
+
});
|
|
68
|
+
return transition;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
logger.error('Failed to update memory state', { error, memoryId, newState });
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Bulk state update operations
|
|
77
|
+
*/
|
|
78
|
+
async bulkUpdateState(memoryIds, operation) {
|
|
79
|
+
const results = [];
|
|
80
|
+
const targetState = this.operationToState(operation);
|
|
81
|
+
for (const memoryId of memoryIds) {
|
|
82
|
+
try {
|
|
83
|
+
const memory = await this.getMemory(memoryId);
|
|
84
|
+
if (!memory) {
|
|
85
|
+
results.push({
|
|
86
|
+
memory_id: memoryId,
|
|
87
|
+
success: false,
|
|
88
|
+
previous_state: MemoryState.ACTIVE,
|
|
89
|
+
new_state: targetState,
|
|
90
|
+
error: 'Memory not found'
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const previousState = memory.state;
|
|
95
|
+
// Skip if already in target state
|
|
96
|
+
if (previousState === targetState) {
|
|
97
|
+
results.push({
|
|
98
|
+
memory_id: memoryId,
|
|
99
|
+
success: true,
|
|
100
|
+
previous_state: previousState,
|
|
101
|
+
new_state: targetState
|
|
102
|
+
});
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// Perform state transition
|
|
106
|
+
await this.updateMemoryState(memoryId, targetState, `Bulk ${operation} operation`);
|
|
107
|
+
results.push({
|
|
108
|
+
memory_id: memoryId,
|
|
109
|
+
success: true,
|
|
110
|
+
previous_state: previousState,
|
|
111
|
+
new_state: targetState
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
results.push({
|
|
116
|
+
memory_id: memoryId,
|
|
117
|
+
success: false,
|
|
118
|
+
previous_state: MemoryState.ACTIVE,
|
|
119
|
+
new_state: targetState,
|
|
120
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
logger.info('Bulk state update completed', {
|
|
125
|
+
operation,
|
|
126
|
+
totalMemories: memoryIds.length,
|
|
127
|
+
successful: results.filter(r => r.success).length,
|
|
128
|
+
failed: results.filter(r => !r.success).length
|
|
129
|
+
});
|
|
130
|
+
return results;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get memory state history
|
|
134
|
+
*/
|
|
135
|
+
getMemoryStateHistory(memoryId) {
|
|
136
|
+
return this.stateTransitions
|
|
137
|
+
.filter(t => t.memory_id === memoryId)
|
|
138
|
+
.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get memories by state
|
|
142
|
+
*/
|
|
143
|
+
async getMemoriesByState(state, userId, appId, limit = 100) {
|
|
144
|
+
try {
|
|
145
|
+
const params = new URLSearchParams({
|
|
146
|
+
state,
|
|
147
|
+
limit: limit.toString(),
|
|
148
|
+
...(userId && { user_id: userId }),
|
|
149
|
+
...(appId && { app_id: appId })
|
|
150
|
+
});
|
|
151
|
+
const memories = await this.callMemoryAPI('GET', `/memory?${params}`);
|
|
152
|
+
return memories.memories || [];
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
logger.error('Failed to get memories by state', { error, state, userId, appId });
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Archive old memories based on policy
|
|
161
|
+
*/
|
|
162
|
+
async archiveOldMemories(beforeDate, userId, appId) {
|
|
163
|
+
try {
|
|
164
|
+
// Get memories created before the specified date
|
|
165
|
+
const params = new URLSearchParams({
|
|
166
|
+
before: beforeDate,
|
|
167
|
+
state: MemoryState.ACTIVE,
|
|
168
|
+
...(userId && { user_id: userId }),
|
|
169
|
+
...(appId && { app_id: appId })
|
|
170
|
+
});
|
|
171
|
+
const memories = await this.callMemoryAPI('GET', `/memory?${params}`);
|
|
172
|
+
const memoryIds = memories.memories?.map((m) => m.id) || [];
|
|
173
|
+
if (memoryIds.length === 0) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
return await this.bulkUpdateState(memoryIds, 'archive');
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
logger.error('Failed to archive old memories', { error, beforeDate, userId, appId });
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Restore memories from archived/paused state
|
|
185
|
+
*/
|
|
186
|
+
async restoreMemories(memoryIds) {
|
|
187
|
+
const results = [];
|
|
188
|
+
for (const memoryId of memoryIds) {
|
|
189
|
+
try {
|
|
190
|
+
const memory = await this.getMemory(memoryId);
|
|
191
|
+
if (!memory) {
|
|
192
|
+
results.push({
|
|
193
|
+
memory_id: memoryId,
|
|
194
|
+
success: false,
|
|
195
|
+
previous_state: MemoryState.ARCHIVED,
|
|
196
|
+
new_state: MemoryState.ACTIVE,
|
|
197
|
+
error: 'Memory not found'
|
|
198
|
+
});
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const previousState = memory.state;
|
|
202
|
+
// Only restore from paused or archived states
|
|
203
|
+
if (previousState !== MemoryState.PAUSED && previousState !== MemoryState.ARCHIVED) {
|
|
204
|
+
results.push({
|
|
205
|
+
memory_id: memoryId,
|
|
206
|
+
success: false,
|
|
207
|
+
previous_state: previousState,
|
|
208
|
+
new_state: MemoryState.ACTIVE,
|
|
209
|
+
error: `Cannot restore from ${previousState} state`
|
|
210
|
+
});
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
await this.updateMemoryState(memoryId, MemoryState.ACTIVE, 'Memory restoration');
|
|
214
|
+
results.push({
|
|
215
|
+
memory_id: memoryId,
|
|
216
|
+
success: true,
|
|
217
|
+
previous_state: previousState,
|
|
218
|
+
new_state: MemoryState.ACTIVE
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
results.push({
|
|
223
|
+
memory_id: memoryId,
|
|
224
|
+
success: false,
|
|
225
|
+
previous_state: MemoryState.ARCHIVED,
|
|
226
|
+
new_state: MemoryState.ACTIVE,
|
|
227
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return results;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Private helper methods
|
|
235
|
+
*/
|
|
236
|
+
isValidTransition(fromState, toState) {
|
|
237
|
+
const validTransitions = {
|
|
238
|
+
[MemoryState.ACTIVE]: [MemoryState.PAUSED, MemoryState.ARCHIVED, MemoryState.DELETED],
|
|
239
|
+
[MemoryState.PAUSED]: [MemoryState.ACTIVE, MemoryState.ARCHIVED, MemoryState.DELETED],
|
|
240
|
+
[MemoryState.ARCHIVED]: [MemoryState.ACTIVE, MemoryState.DELETED],
|
|
241
|
+
[MemoryState.DELETED]: [] // No transitions from deleted state
|
|
242
|
+
};
|
|
243
|
+
return validTransitions[fromState]?.includes(toState) || false;
|
|
244
|
+
}
|
|
245
|
+
operationToState(operation) {
|
|
246
|
+
switch (operation) {
|
|
247
|
+
case 'pause':
|
|
248
|
+
return MemoryState.PAUSED;
|
|
249
|
+
case 'archive':
|
|
250
|
+
return MemoryState.ARCHIVED;
|
|
251
|
+
case 'delete':
|
|
252
|
+
return MemoryState.DELETED;
|
|
253
|
+
default:
|
|
254
|
+
throw new Error(`Unknown operation: ${operation}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async getMemory(memoryId) {
|
|
258
|
+
try {
|
|
259
|
+
const response = await this.callMemoryAPI('GET', `/memory/${memoryId}`);
|
|
260
|
+
return response;
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
logger.error('Failed to get memory', { error, memoryId });
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async updateMemoryViaAPI(memoryId, updates) {
|
|
268
|
+
await this.callMemoryAPI('PUT', `/memory/${memoryId}`, updates);
|
|
269
|
+
}
|
|
270
|
+
async callMemoryAPI(method, endpoint, data) {
|
|
271
|
+
const apiUrl = this.config.get('apiUrl') || 'https://api.lanonasis.com';
|
|
272
|
+
const token = this.config.get('token');
|
|
273
|
+
const axios = (await import('axios')).default;
|
|
274
|
+
const response = await axios({
|
|
275
|
+
method,
|
|
276
|
+
url: `${apiUrl}/api/v1${endpoint}`,
|
|
277
|
+
headers: {
|
|
278
|
+
'Authorization': `Bearer ${token}`,
|
|
279
|
+
'Content-Type': 'application/json'
|
|
280
|
+
},
|
|
281
|
+
data
|
|
282
|
+
});
|
|
283
|
+
return response.data;
|
|
284
|
+
}
|
|
285
|
+
async getCurrentUserId() {
|
|
286
|
+
const token = this.config.get('token');
|
|
287
|
+
if (token && typeof token === 'string') {
|
|
288
|
+
try {
|
|
289
|
+
const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
|
|
290
|
+
return payload.sub || payload.user_id || 'anonymous';
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return 'anonymous';
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return 'anonymous';
|
|
297
|
+
}
|
|
298
|
+
generateTransitionId() {
|
|
299
|
+
return `transition_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
@@ -175,11 +175,11 @@ export declare const ApiKeyRevokeSchema: z.ZodObject<{
|
|
|
175
175
|
key_id: z.ZodString;
|
|
176
176
|
reason: z.ZodOptional<z.ZodString>;
|
|
177
177
|
}, "strip", z.ZodTypeAny, {
|
|
178
|
-
key_id?: string;
|
|
179
178
|
reason?: string;
|
|
180
|
-
}, {
|
|
181
179
|
key_id?: string;
|
|
180
|
+
}, {
|
|
182
181
|
reason?: string;
|
|
182
|
+
key_id?: string;
|
|
183
183
|
}>;
|
|
184
184
|
export declare const SystemHealthSchema: z.ZodObject<{
|
|
185
185
|
verbose: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -217,13 +217,13 @@ export declare const BulkOperationSchema: z.ZodObject<{
|
|
|
217
217
|
transaction: z.ZodDefault<z.ZodBoolean>;
|
|
218
218
|
}, "strip", z.ZodTypeAny, {
|
|
219
219
|
operation?: "create" | "delete" | "update";
|
|
220
|
-
entity_type?: "topic" | "memory" | "apikey";
|
|
221
220
|
items?: Record<string, any>[];
|
|
221
|
+
entity_type?: "topic" | "memory" | "apikey";
|
|
222
222
|
transaction?: boolean;
|
|
223
223
|
}, {
|
|
224
224
|
operation?: "create" | "delete" | "update";
|
|
225
|
-
entity_type?: "topic" | "memory" | "apikey";
|
|
226
225
|
items?: Record<string, any>[];
|
|
226
|
+
entity_type?: "topic" | "memory" | "apikey";
|
|
227
227
|
transaction?: boolean;
|
|
228
228
|
}>;
|
|
229
229
|
export declare const ImportExportSchema: z.ZodObject<{
|
|
@@ -551,11 +551,11 @@ export declare const MCPSchemas: {
|
|
|
551
551
|
key_id: z.ZodString;
|
|
552
552
|
reason: z.ZodOptional<z.ZodString>;
|
|
553
553
|
}, "strip", z.ZodTypeAny, {
|
|
554
|
-
key_id?: string;
|
|
555
554
|
reason?: string;
|
|
556
|
-
}, {
|
|
557
555
|
key_id?: string;
|
|
556
|
+
}, {
|
|
558
557
|
reason?: string;
|
|
558
|
+
key_id?: string;
|
|
559
559
|
}>;
|
|
560
560
|
};
|
|
561
561
|
system: {
|
|
@@ -597,13 +597,13 @@ export declare const MCPSchemas: {
|
|
|
597
597
|
transaction: z.ZodDefault<z.ZodBoolean>;
|
|
598
598
|
}, "strip", z.ZodTypeAny, {
|
|
599
599
|
operation?: "create" | "delete" | "update";
|
|
600
|
-
entity_type?: "topic" | "memory" | "apikey";
|
|
601
600
|
items?: Record<string, any>[];
|
|
601
|
+
entity_type?: "topic" | "memory" | "apikey";
|
|
602
602
|
transaction?: boolean;
|
|
603
603
|
}, {
|
|
604
604
|
operation?: "create" | "delete" | "update";
|
|
605
|
-
entity_type?: "topic" | "memory" | "apikey";
|
|
606
605
|
items?: Record<string, any>[];
|
|
606
|
+
entity_type?: "topic" | "memory" | "apikey";
|
|
607
607
|
transaction?: boolean;
|
|
608
608
|
}>;
|
|
609
609
|
importExport: z.ZodObject<{
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector Store Integration
|
|
3
|
+
* Supports multiple vector stores with configurable embedding models
|
|
4
|
+
*/
|
|
5
|
+
export interface VectorStoreConfig {
|
|
6
|
+
provider: 'local' | 'qdrant' | 'chroma';
|
|
7
|
+
url?: string;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
collection?: string;
|
|
10
|
+
dimensions?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface SearchResult {
|
|
13
|
+
id: string;
|
|
14
|
+
score: number;
|
|
15
|
+
metadata: any;
|
|
16
|
+
}
|
|
17
|
+
export declare class LanonasisVectorStore {
|
|
18
|
+
private config;
|
|
19
|
+
private storeConfig;
|
|
20
|
+
private isInitialized;
|
|
21
|
+
private localEmbeddings;
|
|
22
|
+
constructor();
|
|
23
|
+
initialize(): Promise<void>;
|
|
24
|
+
isConfigured(): boolean;
|
|
25
|
+
addMemory(memoryId: string, content: string, metadata: any): Promise<void>;
|
|
26
|
+
searchMemories(query: string, options?: any): Promise<SearchResult[]>;
|
|
27
|
+
findRelatedMemories(memoryId: string, options?: any): Promise<SearchResult[]>;
|
|
28
|
+
private generateSimpleEmbedding;
|
|
29
|
+
private simpleHash;
|
|
30
|
+
private cosineSimilarity;
|
|
31
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector Store Integration
|
|
3
|
+
* Supports multiple vector stores with configurable embedding models
|
|
4
|
+
*/
|
|
5
|
+
import { CLIConfig } from '../utils/config.js';
|
|
6
|
+
import { logger } from './logger.js';
|
|
7
|
+
export class LanonasisVectorStore {
|
|
8
|
+
config;
|
|
9
|
+
storeConfig;
|
|
10
|
+
isInitialized = false;
|
|
11
|
+
localEmbeddings = new Map();
|
|
12
|
+
constructor() {
|
|
13
|
+
this.config = new CLIConfig();
|
|
14
|
+
this.storeConfig = {
|
|
15
|
+
provider: 'local',
|
|
16
|
+
collection: 'lanonasis_memories',
|
|
17
|
+
dimensions: 384
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
async initialize() {
|
|
21
|
+
this.isInitialized = true;
|
|
22
|
+
logger.info('Vector store initialized', { provider: this.storeConfig.provider });
|
|
23
|
+
}
|
|
24
|
+
isConfigured() {
|
|
25
|
+
return this.isInitialized;
|
|
26
|
+
}
|
|
27
|
+
async addMemory(memoryId, content, metadata) {
|
|
28
|
+
const embedding = this.generateSimpleEmbedding(content);
|
|
29
|
+
this.localEmbeddings.set(memoryId, { embedding, metadata, content });
|
|
30
|
+
logger.debug('Memory added to vector store', { memoryId });
|
|
31
|
+
}
|
|
32
|
+
async searchMemories(query, options = {}) {
|
|
33
|
+
const queryEmbedding = this.generateSimpleEmbedding(query);
|
|
34
|
+
const results = [];
|
|
35
|
+
// Only consider memories the caller is allowed to see
|
|
36
|
+
const allowedIds = options.memoryIds
|
|
37
|
+
? new Set(options.memoryIds)
|
|
38
|
+
: undefined;
|
|
39
|
+
for (const [id, data] of this.localEmbeddings) {
|
|
40
|
+
if (allowedIds && !allowedIds.has(id))
|
|
41
|
+
continue;
|
|
42
|
+
const similarity = this.cosineSimilarity(queryEmbedding, data.embedding);
|
|
43
|
+
if (similarity >= (options.threshold || 0.7)) {
|
|
44
|
+
results.push({ id, score: similarity, metadata: data.metadata });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return results
|
|
48
|
+
.sort((a, b) => b.score - a.score)
|
|
49
|
+
.slice(0, options.limit || 10);
|
|
50
|
+
}
|
|
51
|
+
async findRelatedMemories(memoryId, options = {}) {
|
|
52
|
+
const memory = this.localEmbeddings.get(memoryId);
|
|
53
|
+
if (!memory)
|
|
54
|
+
return [];
|
|
55
|
+
const results = [];
|
|
56
|
+
for (const [id, data] of this.localEmbeddings) {
|
|
57
|
+
if (id === memoryId)
|
|
58
|
+
continue;
|
|
59
|
+
const similarity = this.cosineSimilarity(memory.embedding, data.embedding);
|
|
60
|
+
if (similarity >= (options.threshold || 0.6)) {
|
|
61
|
+
results.push({ id, score: similarity, metadata: data.metadata });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return results.sort((a, b) => b.score - a.score).slice(0, options.limit || 5);
|
|
65
|
+
}
|
|
66
|
+
generateSimpleEmbedding(text) {
|
|
67
|
+
const words = text.toLowerCase().split(/\s+/);
|
|
68
|
+
const embedding = new Array(this.storeConfig.dimensions ?? 384).fill(0);
|
|
69
|
+
words.forEach((word, index) => {
|
|
70
|
+
const hash = this.simpleHash(word);
|
|
71
|
+
const position = Math.abs(hash) % embedding.length;
|
|
72
|
+
embedding[position] += 1 / (index + 1);
|
|
73
|
+
});
|
|
74
|
+
const magnitude = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));
|
|
75
|
+
return embedding.map(val => magnitude > 0 ? val / magnitude : 0);
|
|
76
|
+
}
|
|
77
|
+
simpleHash(str) {
|
|
78
|
+
let hash = 0;
|
|
79
|
+
for (let i = 0; i < str.length; i++) {
|
|
80
|
+
const char = str.charCodeAt(i);
|
|
81
|
+
hash = ((hash << 5) - hash) + char;
|
|
82
|
+
hash = hash & hash;
|
|
83
|
+
}
|
|
84
|
+
return hash;
|
|
85
|
+
}
|
|
86
|
+
cosineSimilarity(a, b) {
|
|
87
|
+
const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
|
|
88
|
+
const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
|
|
89
|
+
const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
|
|
90
|
+
return magnitudeA && magnitudeB ? dotProduct / (magnitudeA * magnitudeB) : 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
package/package.json
CHANGED