@ai.ntellect/core 0.5.0 → 0.6.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/.mocharc.json +1 -1
- package/README.md +311 -272
- package/dist/graph/controller.js +63 -0
- package/dist/graph/engine.js +563 -0
- package/dist/index.js +6 -6
- package/dist/memory/adapters/meilisearch/index.js +249 -0
- package/dist/memory/adapters/redis/index.js +96 -0
- package/dist/memory/index.js +9 -0
- package/dist/services/agenda.js +115 -0
- package/dist/services/embedding.js +40 -0
- package/dist/services/queue.js +99 -103
- package/dist/test/graph/controller.test.js +170 -0
- package/dist/test/graph/engine.test.js +465 -0
- package/dist/test/memory/adapters/meilisearch.test.js +250 -0
- package/dist/test/memory/adapters/redis.test.js +143 -0
- package/dist/test/memory/base.test.js +209 -0
- package/dist/test/services/agenda.test.js +230 -0
- package/dist/test/services/queue.test.js +258 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/generate-object.js +32 -11
- package/dist/utils/inject-actions.js +2 -2
- package/dist/utils/queue-item-transformer.js +2 -2
- package/dist/utils/state-manager.js +20 -0
- package/graph/controller.ts +60 -0
- package/{services/workflow.ts → graph/engine.ts} +331 -113
- package/index.ts +7 -7
- package/interfaces/index.ts +119 -0
- package/memory/adapters/meilisearch/index.ts +286 -0
- package/memory/adapters/redis/index.ts +103 -0
- package/memory/index.ts +22 -0
- package/package.json +7 -2
- package/services/agenda.ts +48 -43
- package/services/embedding.ts +26 -0
- package/services/queue.ts +2 -29
- package/test/.env.test +4 -0
- package/test/graph/controller.test.ts +186 -0
- package/test/graph/engine.test.ts +563 -0
- package/test/memory/adapters/meilisearch.test.ts +297 -0
- package/test/memory/adapters/redis.test.ts +160 -0
- package/test/memory/base.test.ts +229 -0
- package/test/services/agenda.test.ts +280 -0
- package/test/services/queue.test.ts +286 -44
- package/tsconfig.json +10 -10
- package/types/index.ts +270 -0
- package/utils/generate-object.js +111 -0
- package/utils/header-builder.js +34 -0
- package/utils/inject-actions.js +16 -0
- package/utils/queue-item-transformer.js +24 -0
- package/utils/queue-item-transformer.ts +8 -11
- package/utils/sanitize-results.js +60 -0
- package/utils/schema-generator.js +46 -0
- package/utils/state-manager.js +20 -0
- package/utils/state-manager.ts +17 -12
- package/.nvmrc +0 -1
- package/README.FR.md +0 -916
- package/agent/index.ts +0 -151
- package/agent/workflow/conditions.ts +0 -16
- package/agent/workflow/handlers/interpreter.handler.ts +0 -48
- package/agent/workflow/handlers/memory.handler.ts +0 -106
- package/agent/workflow/handlers/orchestrator.handler.ts +0 -23
- package/agent/workflow/handlers/queue.handler.ts +0 -34
- package/agent/workflow/handlers/scheduler.handler.ts +0 -61
- package/agent/workflow/index.ts +0 -62
- package/dist/agent/index.d.ts +0 -38
- package/dist/agent/index.js +0 -143
- package/dist/agent/tools/get-rss.d.ts +0 -16
- package/dist/agent/tools/get-rss.js +0 -62
- package/dist/bull.d.ts +0 -1
- package/dist/bull.js +0 -9
- package/dist/examples/index.d.ts +0 -2
- package/dist/examples/index.js +0 -89
- package/dist/index.d.ts +0 -7
- package/dist/llm/interpreter/context.d.ts +0 -15
- package/dist/llm/interpreter/context.js +0 -89
- package/dist/llm/interpreter/index.d.ts +0 -21
- package/dist/llm/interpreter/index.js +0 -87
- package/dist/llm/memory-manager/context.d.ts +0 -2
- package/dist/llm/memory-manager/context.js +0 -22
- package/dist/llm/memory-manager/index.d.ts +0 -17
- package/dist/llm/memory-manager/index.js +0 -107
- package/dist/llm/orchestrator/context.d.ts +0 -2
- package/dist/llm/orchestrator/context.js +0 -23
- package/dist/llm/orchestrator/index.d.ts +0 -44
- package/dist/llm/orchestrator/index.js +0 -139
- package/dist/llm/orchestrator/types.d.ts +0 -12
- package/dist/memory/cache.d.ts +0 -22
- package/dist/memory/cache.js +0 -165
- package/dist/memory/persistent.d.ts +0 -57
- package/dist/memory/persistent.js +0 -189
- package/dist/services/queue.d.ts +0 -13
- package/dist/services/redis-cache.d.ts +0 -37
- package/dist/services/redis-cache.js +0 -93
- package/dist/services/scheduler.d.ts +0 -40
- package/dist/services/scheduler.js +0 -99
- package/dist/services/telegram-monitor.d.ts +0 -0
- package/dist/services/telegram-monitor.js +0 -118
- package/dist/t.d.ts +0 -46
- package/dist/t.js +0 -102
- package/dist/test.d.ts +0 -0
- package/dist/test.js +0 -438
- package/dist/types.d.ts +0 -258
- package/dist/types.js +0 -22
- package/dist/utils/generate-object.d.ts +0 -12
- package/dist/utils/header-builder.d.ts +0 -11
- package/dist/utils/inject-actions.d.ts +0 -2
- package/dist/utils/queue-item-transformer.d.ts +0 -7
- package/dist/utils/sanitize-results.d.ts +0 -17
- package/dist/utils/schema-generator.d.ts +0 -16
- package/examples/actions/get-rss.ts +0 -71
- package/examples/index.ts +0 -98
- package/index.html +0 -42
- package/llm/dynamic-condition/example.ts +0 -36
- package/llm/dynamic-condition/index.ts +0 -108
- package/llm/interpreter/context.ts +0 -94
- package/llm/interpreter/index.ts +0 -140
- package/llm/memory-manager/context.ts +0 -19
- package/llm/memory-manager/index.ts +0 -115
- package/llm/orchestrator/context.ts +0 -19
- package/llm/orchestrator/index.ts +0 -192
- package/llm/orchestrator/types.ts +0 -14
- package/memory/cache.ts +0 -221
- package/memory/persistent.ts +0 -265
- package/script.js +0 -167
- package/services/cache.ts +0 -298
- package/services/telegram-monitor.ts +0 -138
- package/t.py +0 -79
- package/t.ts +0 -25
- package/test/llm/orchestrator.test.ts +0 -47
- package/test/llm/synthesizer.test.ts +0 -31
- package/types.ts +0 -367
- /package/dist/{llm/orchestrator/types.js → interfaces/index.js} +0 -0
@@ -0,0 +1,119 @@
|
|
1
|
+
import { BaseMemoryType } from "@/types";
|
2
|
+
import { SharedState } from "@/types";
|
3
|
+
|
4
|
+
/* ======================== PERSISTENCE ======================== */
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Interface for persisting graph execution state.
|
8
|
+
*/
|
9
|
+
export interface Persistence<T> {
|
10
|
+
saveState(
|
11
|
+
graphName: string,
|
12
|
+
state: SharedState<T>,
|
13
|
+
currentNode: string
|
14
|
+
): Promise<void>;
|
15
|
+
loadState(
|
16
|
+
graphName: string
|
17
|
+
): Promise<{ state: SharedState<T>; currentNode: string } | null>;
|
18
|
+
}
|
19
|
+
|
20
|
+
/* ======================== REAL-TIME NOTIFICATIONS ======================== */
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Interface for real-time notifications.
|
24
|
+
*/
|
25
|
+
export interface RealTimeNotifier {
|
26
|
+
notify(event: string, data: any): void;
|
27
|
+
}
|
28
|
+
|
29
|
+
/* ======================== EMBEDDING SERVICE ======================== */
|
30
|
+
|
31
|
+
/**
|
32
|
+
* Interface for an embedding service that processes text into vector representations.
|
33
|
+
*/
|
34
|
+
export interface EmbeddingService {
|
35
|
+
/**
|
36
|
+
* Generates an embedding for a single text.
|
37
|
+
* @param {string} text - The input text to embed.
|
38
|
+
* @returns {Promise<number[]>} - A vector representation of the text.
|
39
|
+
*/
|
40
|
+
embedText(text: string): Promise<number[]>;
|
41
|
+
|
42
|
+
/**
|
43
|
+
* Generates embeddings for multiple texts at once.
|
44
|
+
* @param {string[]} texts - The array of texts to embed.
|
45
|
+
* @returns {Promise<number[][]>} - A list of vector representations.
|
46
|
+
*/
|
47
|
+
embedMany(texts: string[]): Promise<number[][]>;
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Calculates the similarity between two embeddings.
|
51
|
+
* @param {number[]} embedding1 - First vector.
|
52
|
+
* @param {number[]} embedding2 - Second vector.
|
53
|
+
* @returns {number} - A similarity score between the two vectors.
|
54
|
+
*/
|
55
|
+
calculateSimilarity(embedding1: number[], embedding2: number[]): number;
|
56
|
+
}
|
57
|
+
|
58
|
+
/* ======================== MEMORY SERVICE ======================== */
|
59
|
+
|
60
|
+
/**
|
61
|
+
* Interface for managing memory storage and retrieval.
|
62
|
+
*/
|
63
|
+
export interface BaseMemoryService {
|
64
|
+
/**
|
65
|
+
* Initializes the memory storage connection.
|
66
|
+
* @returns {Promise<void>} - Resolves when initialization is complete.
|
67
|
+
*/
|
68
|
+
initializeConnection(): Promise<void>;
|
69
|
+
|
70
|
+
/**
|
71
|
+
* Stores a new memory entry.
|
72
|
+
* @param {BaseMemoryType} memory - The memory data to store.
|
73
|
+
* @param {number} [ttl] - Optional time-to-live in seconds.
|
74
|
+
* @returns {Promise<void>}
|
75
|
+
*/
|
76
|
+
createMemory(memory: BaseMemoryType, ttl?: number): Promise<void>;
|
77
|
+
|
78
|
+
/**
|
79
|
+
* Retrieves a memory entry by its unique ID.
|
80
|
+
* @param {string} id - The memory entry identifier.
|
81
|
+
* @returns {Promise<BaseMemoryType | null>} - The found memory or null.
|
82
|
+
*/
|
83
|
+
getMemoryById(id: string): Promise<BaseMemoryType | null>;
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Searches for memory entries based on a query and optional constraints.
|
87
|
+
* @param {string} query - The search query.
|
88
|
+
* @param {Object} options - Search options.
|
89
|
+
* @param {string} options.roomId - The room identifier.
|
90
|
+
* @param {number} [options.limit] - Maximum number of results (optional).
|
91
|
+
* @returns {Promise<BaseMemoryType[]>} - A list of matched memory entries.
|
92
|
+
*/
|
93
|
+
getMemoryByIndex(
|
94
|
+
query: string,
|
95
|
+
options: {
|
96
|
+
roomId: string;
|
97
|
+
limit?: number;
|
98
|
+
}
|
99
|
+
): Promise<BaseMemoryType[]>;
|
100
|
+
|
101
|
+
/**
|
102
|
+
* Retrieves all stored memory entries.
|
103
|
+
* @returns {Promise<BaseMemoryType[]>} - A list of all memory entries.
|
104
|
+
*/
|
105
|
+
getAllMemories(): Promise<BaseMemoryType[]>;
|
106
|
+
|
107
|
+
/**
|
108
|
+
* Deletes a memory entry by its unique ID.
|
109
|
+
* @param {string} id - The memory entry identifier.
|
110
|
+
* @returns {Promise<void>}
|
111
|
+
*/
|
112
|
+
clearMemoryById(id: string): Promise<void>;
|
113
|
+
|
114
|
+
/**
|
115
|
+
* Clears all stored memory entries.
|
116
|
+
* @returns {Promise<void>}
|
117
|
+
*/
|
118
|
+
clearAllMemories(): Promise<void>;
|
119
|
+
}
|
@@ -0,0 +1,286 @@
|
|
1
|
+
import { BaseMemoryService } from "@/interfaces";
|
2
|
+
import { BaseMemory } from "@/memory";
|
3
|
+
import { BaseMemoryType, CreateMemoryInput, MeilisearchConfig } from "@/types";
|
4
|
+
|
5
|
+
export class MeilisearchAdapter extends BaseMemory {
|
6
|
+
constructor(
|
7
|
+
private readonly config: MeilisearchConfig,
|
8
|
+
baseMemoryService: BaseMemoryService
|
9
|
+
) {
|
10
|
+
super(baseMemoryService);
|
11
|
+
}
|
12
|
+
|
13
|
+
private async makeRequest(path: string, options?: RequestInit) {
|
14
|
+
try {
|
15
|
+
const url = `${this.config.host}${path}`;
|
16
|
+
const response = await fetch(url, {
|
17
|
+
...options,
|
18
|
+
headers: {
|
19
|
+
"Content-Type": "application/json",
|
20
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
21
|
+
...options?.headers,
|
22
|
+
},
|
23
|
+
});
|
24
|
+
|
25
|
+
if (!response.ok) {
|
26
|
+
const errorBody = await response.text();
|
27
|
+
throw new Error(
|
28
|
+
`HTTP ${response.status}: ${errorBody || response.statusText}`
|
29
|
+
);
|
30
|
+
}
|
31
|
+
|
32
|
+
return response.json();
|
33
|
+
} catch (error) {
|
34
|
+
if (error instanceof TypeError && error.message === "Failed to fetch") {
|
35
|
+
throw new Error(
|
36
|
+
`Network error: Unable to connect to Meilisearch at ${this.config.host}`
|
37
|
+
);
|
38
|
+
}
|
39
|
+
throw error;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
async initializeStorage(roomId: string): Promise<void> {
|
44
|
+
try {
|
45
|
+
let indexExists = false;
|
46
|
+
|
47
|
+
try {
|
48
|
+
// Check if index exists
|
49
|
+
await this.makeRequest(`/indexes/${roomId}`);
|
50
|
+
indexExists = true;
|
51
|
+
} catch (error) {
|
52
|
+
// Only continue if the error is "Not found"
|
53
|
+
if (!(error instanceof Error && error.message.includes("Not found"))) {
|
54
|
+
throw error;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
if (!indexExists) {
|
59
|
+
// Create new index
|
60
|
+
await this.makeRequest("/indexes", {
|
61
|
+
method: "POST",
|
62
|
+
body: JSON.stringify({
|
63
|
+
uid: roomId,
|
64
|
+
primaryKey: "id",
|
65
|
+
}),
|
66
|
+
});
|
67
|
+
|
68
|
+
// Wait for index creation
|
69
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
70
|
+
}
|
71
|
+
|
72
|
+
// Update settings
|
73
|
+
await this.makeRequest(`/indexes/${roomId}/settings`, {
|
74
|
+
method: "PATCH",
|
75
|
+
body: JSON.stringify({
|
76
|
+
searchableAttributes: this.config.searchableAttributes || [
|
77
|
+
"data",
|
78
|
+
"query",
|
79
|
+
],
|
80
|
+
sortableAttributes: this.config.sortableAttributes || ["createdAt"],
|
81
|
+
}),
|
82
|
+
});
|
83
|
+
} catch (error) {
|
84
|
+
const errorMessage =
|
85
|
+
error instanceof Error ? error.message : "Unknown error";
|
86
|
+
console.error(
|
87
|
+
`Error initializing storage for index ${roomId}:`,
|
88
|
+
errorMessage
|
89
|
+
);
|
90
|
+
throw new Error(
|
91
|
+
`Failed to initialize storage for index ${roomId}: ${errorMessage}`
|
92
|
+
);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
async addDocuments(
|
97
|
+
documents: BaseMemoryType[],
|
98
|
+
roomId: string
|
99
|
+
): Promise<void> {
|
100
|
+
await this.makeRequest(`/indexes/${roomId}/documents`, {
|
101
|
+
method: "POST",
|
102
|
+
body: JSON.stringify(documents),
|
103
|
+
});
|
104
|
+
}
|
105
|
+
|
106
|
+
async search(
|
107
|
+
query: string,
|
108
|
+
roomId: string,
|
109
|
+
options?: { limit?: number; threshold?: number }
|
110
|
+
): Promise<SearchResult[]> {
|
111
|
+
const searchResults = await this.makeRequest(`/indexes/${roomId}/search`, {
|
112
|
+
method: "POST",
|
113
|
+
body: JSON.stringify({
|
114
|
+
q: query,
|
115
|
+
limit: options?.limit || 10,
|
116
|
+
}),
|
117
|
+
});
|
118
|
+
|
119
|
+
return searchResults.hits.map((hit: any) => ({
|
120
|
+
document: {
|
121
|
+
id: hit.id,
|
122
|
+
data: hit.data,
|
123
|
+
query: hit.query,
|
124
|
+
embedding: hit.embedding,
|
125
|
+
roomId: hit.roomId,
|
126
|
+
createdAt: hit.createdAt,
|
127
|
+
},
|
128
|
+
score: hit._score || 0,
|
129
|
+
}));
|
130
|
+
}
|
131
|
+
|
132
|
+
async deleteStorage(roomId: string): Promise<void> {
|
133
|
+
await this.makeRequest(`/indexes/${roomId}`, {
|
134
|
+
method: "DELETE",
|
135
|
+
});
|
136
|
+
}
|
137
|
+
|
138
|
+
// Required BaseMemory implementations
|
139
|
+
async init(): Promise<void> {
|
140
|
+
try {
|
141
|
+
// Initialize the default "memories" index
|
142
|
+
await this.initializeStorage("memories");
|
143
|
+
} catch (error) {
|
144
|
+
const errorMessage =
|
145
|
+
error instanceof Error ? error.message : "Unknown error";
|
146
|
+
console.error("Failed to initialize default index:", errorMessage);
|
147
|
+
throw new Error(`Failed to initialize default index: ${errorMessage}`);
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
async createMemory(
|
152
|
+
input: CreateMemoryInput & { embedding?: number[] }
|
153
|
+
): Promise<BaseMemoryType | undefined> {
|
154
|
+
// Initialize storage for this roomId if needed
|
155
|
+
await this.initializeStorage(input.roomId);
|
156
|
+
|
157
|
+
// Search for existing memory with same data and query
|
158
|
+
const searchResults = await this.search(input.data, input.roomId, {
|
159
|
+
limit: 1,
|
160
|
+
});
|
161
|
+
const existingMemory = searchResults.find(
|
162
|
+
(result) =>
|
163
|
+
result.document.data === input.data &&
|
164
|
+
result.document.query === input.query &&
|
165
|
+
result.document.roomId === input.roomId
|
166
|
+
);
|
167
|
+
|
168
|
+
// If found, return existing memory
|
169
|
+
if (existingMemory) {
|
170
|
+
return existingMemory.document;
|
171
|
+
}
|
172
|
+
|
173
|
+
// If not found, create new memory
|
174
|
+
const memory: BaseMemoryType = {
|
175
|
+
id: crypto.randomUUID(),
|
176
|
+
data: input.data,
|
177
|
+
query: input.query,
|
178
|
+
embedding: input.embedding || null,
|
179
|
+
roomId: input.roomId,
|
180
|
+
createdAt: new Date(),
|
181
|
+
};
|
182
|
+
|
183
|
+
await this.addDocuments([memory], input.roomId);
|
184
|
+
return memory;
|
185
|
+
}
|
186
|
+
|
187
|
+
async getMemoryById(
|
188
|
+
id: string,
|
189
|
+
roomId: string
|
190
|
+
): Promise<BaseMemoryType | null> {
|
191
|
+
try {
|
192
|
+
const result = await this.makeRequest(
|
193
|
+
`/indexes/${roomId}/documents/${id}`
|
194
|
+
);
|
195
|
+
return result
|
196
|
+
? {
|
197
|
+
id: result.id,
|
198
|
+
data: result.data,
|
199
|
+
query: result.query,
|
200
|
+
embedding: result.embedding,
|
201
|
+
roomId: result.roomId,
|
202
|
+
createdAt: result.createdAt,
|
203
|
+
}
|
204
|
+
: null;
|
205
|
+
} catch {
|
206
|
+
return null;
|
207
|
+
}
|
208
|
+
}
|
209
|
+
|
210
|
+
async getMemoryByIndex(
|
211
|
+
query: string,
|
212
|
+
options: { roomId: string; limit?: number }
|
213
|
+
): Promise<BaseMemoryType[]> {
|
214
|
+
const results = await this.search(query, options.roomId, {
|
215
|
+
limit: options.limit,
|
216
|
+
});
|
217
|
+
return results
|
218
|
+
.filter((result) => result.document.roomId === options.roomId)
|
219
|
+
.map((result) => ({
|
220
|
+
id: result.document.id,
|
221
|
+
data: result.document.data,
|
222
|
+
query: result.document.query,
|
223
|
+
embedding: result.document.embedding,
|
224
|
+
roomId: result.document.roomId,
|
225
|
+
createdAt: result.document.createdAt,
|
226
|
+
}));
|
227
|
+
}
|
228
|
+
|
229
|
+
async getAllMemories(roomId: string): Promise<BaseMemoryType[]> {
|
230
|
+
const results = await this.makeRequest(`/indexes/${roomId}/documents`);
|
231
|
+
return results.map((doc: any) => ({
|
232
|
+
id: doc.id,
|
233
|
+
data: doc.data,
|
234
|
+
query: doc.query,
|
235
|
+
embedding: doc.embedding,
|
236
|
+
roomId: doc.roomId,
|
237
|
+
createdAt: doc.createdAt,
|
238
|
+
}));
|
239
|
+
}
|
240
|
+
|
241
|
+
async clearMemoryById(id: string, roomId: string): Promise<void> {
|
242
|
+
try {
|
243
|
+
// Ensure the index exists before attempting to delete
|
244
|
+
await this.initializeStorage(roomId);
|
245
|
+
|
246
|
+
await this.makeRequest(`/indexes/${roomId}/documents/${id}`, {
|
247
|
+
method: "DELETE",
|
248
|
+
});
|
249
|
+
} catch (error) {
|
250
|
+
const errorMessage =
|
251
|
+
error instanceof Error ? error.message : "Unknown error";
|
252
|
+
console.error(
|
253
|
+
`Error clearing memory ${id} from index ${roomId}:`,
|
254
|
+
errorMessage
|
255
|
+
);
|
256
|
+
throw new Error(
|
257
|
+
`Failed to clear memory ${id} from index ${roomId}: ${errorMessage}`
|
258
|
+
);
|
259
|
+
}
|
260
|
+
}
|
261
|
+
|
262
|
+
async clearAllMemories(): Promise<void> {
|
263
|
+
try {
|
264
|
+
// Get all indexes
|
265
|
+
const response = await this.makeRequest("/indexes");
|
266
|
+
const indexes = response.results || [];
|
267
|
+
|
268
|
+
// Delete each index
|
269
|
+
for (const index of indexes) {
|
270
|
+
await this.deleteStorage(index.uid);
|
271
|
+
}
|
272
|
+
|
273
|
+
// Reinitialize the default index
|
274
|
+
await this.init();
|
275
|
+
} catch (error) {
|
276
|
+
const errorMessage =
|
277
|
+
error instanceof Error ? error.message : "Unknown error";
|
278
|
+
throw new Error(`Failed to clear all memories: ${errorMessage}`);
|
279
|
+
}
|
280
|
+
}
|
281
|
+
}
|
282
|
+
|
283
|
+
interface SearchResult {
|
284
|
+
document: BaseMemoryType;
|
285
|
+
score: number;
|
286
|
+
}
|
@@ -0,0 +1,103 @@
|
|
1
|
+
import { BaseMemoryService } from "@/interfaces";
|
2
|
+
import { BaseMemoryType } from "@/types";
|
3
|
+
import { createClient } from "redis";
|
4
|
+
|
5
|
+
export class RedisAdapter implements BaseMemoryService {
|
6
|
+
private redis;
|
7
|
+
private readonly cachePrefix: string;
|
8
|
+
private readonly cacheTTL: number;
|
9
|
+
|
10
|
+
constructor(
|
11
|
+
private readonly redisUrl: string,
|
12
|
+
options: {
|
13
|
+
cachePrefix?: string;
|
14
|
+
cacheTTL?: number;
|
15
|
+
}
|
16
|
+
) {
|
17
|
+
this.cachePrefix = options.cachePrefix || "memory:";
|
18
|
+
this.cacheTTL = options.cacheTTL || 3600;
|
19
|
+
this.redis = createClient({
|
20
|
+
url: redisUrl,
|
21
|
+
socket: {
|
22
|
+
tls: true,
|
23
|
+
rejectUnauthorized: true,
|
24
|
+
},
|
25
|
+
});
|
26
|
+
}
|
27
|
+
|
28
|
+
async initializeConnection(): Promise<void> {
|
29
|
+
this.redis.on("error", (err) => console.error("Redis Client Error:", err));
|
30
|
+
await this.redis.connect();
|
31
|
+
}
|
32
|
+
|
33
|
+
async createMemory(memory: BaseMemoryType, ttl?: number): Promise<void> {
|
34
|
+
const key = memory.roomId
|
35
|
+
? `${this.cachePrefix}${memory.roomId}:${memory.id}`
|
36
|
+
: `${this.cachePrefix}${memory.id}`;
|
37
|
+
|
38
|
+
await this.redis.set(key, JSON.stringify(memory), {
|
39
|
+
EX: ttl || this.cacheTTL,
|
40
|
+
});
|
41
|
+
}
|
42
|
+
|
43
|
+
async getMemoryById(
|
44
|
+
id: string,
|
45
|
+
roomId?: string
|
46
|
+
): Promise<BaseMemoryType | null> {
|
47
|
+
const key = roomId
|
48
|
+
? `${this.cachePrefix}${roomId}:${id}`
|
49
|
+
: `${this.cachePrefix}${id}`;
|
50
|
+
|
51
|
+
const data = await this.redis.get(key);
|
52
|
+
return data ? JSON.parse(data) : null;
|
53
|
+
}
|
54
|
+
|
55
|
+
async getMemoryByIndex(
|
56
|
+
query: string,
|
57
|
+
options: {
|
58
|
+
roomId?: string;
|
59
|
+
limit?: number;
|
60
|
+
} = {}
|
61
|
+
): Promise<BaseMemoryType[]> {
|
62
|
+
const pattern = options.roomId
|
63
|
+
? `${this.cachePrefix}${options.roomId}:*`
|
64
|
+
: `${this.cachePrefix}*`;
|
65
|
+
|
66
|
+
const keys = await this.redis.keys(pattern);
|
67
|
+
const memories = await Promise.all(
|
68
|
+
keys.map(async (key) => {
|
69
|
+
const data = await this.redis.get(key);
|
70
|
+
return data ? JSON.parse(data) : null;
|
71
|
+
})
|
72
|
+
);
|
73
|
+
return memories.filter(Boolean).slice(0, options.limit || 10);
|
74
|
+
}
|
75
|
+
|
76
|
+
async getAllMemories(): Promise<BaseMemoryType[]> {
|
77
|
+
const keys = await this.redis.keys(`${this.cachePrefix}*`);
|
78
|
+
const memories = await Promise.all(
|
79
|
+
keys.map(async (key) => {
|
80
|
+
const data = await this.redis.get(key);
|
81
|
+
return data ? JSON.parse(data) : null;
|
82
|
+
})
|
83
|
+
);
|
84
|
+
return memories.filter(Boolean);
|
85
|
+
}
|
86
|
+
|
87
|
+
async clearMemoryById(id: string): Promise<void> {
|
88
|
+
await this.redis.del(`${this.cachePrefix}${id}`);
|
89
|
+
}
|
90
|
+
|
91
|
+
async clearAllMemories(): Promise<void> {
|
92
|
+
const keys = await this.redis.keys(`${this.cachePrefix}*`);
|
93
|
+
if (keys.length > 0) {
|
94
|
+
await this.redis.del(keys);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
async quit(): Promise<void> {
|
99
|
+
if (this.redis) {
|
100
|
+
await this.redis.quit();
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
package/memory/index.ts
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
import { BaseMemoryService } from "@/interfaces";
|
2
|
+
import { BaseMemoryType, CreateMemoryInput } from "@/types";
|
3
|
+
|
4
|
+
export abstract class BaseMemory {
|
5
|
+
constructor(protected readonly cacheService: BaseMemoryService) {}
|
6
|
+
|
7
|
+
abstract init(): Promise<void>;
|
8
|
+
abstract createMemory(
|
9
|
+
input: CreateMemoryInput & { embedding?: number[] }
|
10
|
+
): Promise<BaseMemoryType | undefined>;
|
11
|
+
abstract getMemoryById(
|
12
|
+
id: string,
|
13
|
+
roomId: string
|
14
|
+
): Promise<BaseMemoryType | null>;
|
15
|
+
abstract getMemoryByIndex(
|
16
|
+
query: string,
|
17
|
+
options: { roomId: string; limit?: number }
|
18
|
+
): Promise<BaseMemoryType[]>;
|
19
|
+
abstract getAllMemories(roomId: string): Promise<BaseMemoryType[]>;
|
20
|
+
abstract clearMemoryById(id: string, roomId: string): Promise<void>;
|
21
|
+
abstract clearAllMemories(): Promise<void>;
|
22
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ai.ntellect/core",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.6.0",
|
4
4
|
"description": "",
|
5
5
|
"main": "dist/index.js",
|
6
6
|
"scripts": {
|
@@ -24,6 +24,7 @@
|
|
24
24
|
"readline": "^1.3.0",
|
25
25
|
"redis": "^4.7.0",
|
26
26
|
"rss-parser": "^3.13.0",
|
27
|
+
"sinon": "^19.0.2",
|
27
28
|
"ws": "^8.18.0",
|
28
29
|
"zod": "^3.24.1"
|
29
30
|
},
|
@@ -31,12 +32,16 @@
|
|
31
32
|
"@jest/globals": "^29.7.0",
|
32
33
|
"@types/chai": "^4.3.20",
|
33
34
|
"@types/mocha": "^10.0.0",
|
35
|
+
"@types/sinon": "^17.0.3",
|
34
36
|
"@types/ws": "^8.5.14",
|
35
37
|
"chai": "^4.5.0",
|
36
38
|
"mocha": "^10.0.0",
|
37
39
|
"ts-node": "^10.9.0",
|
38
40
|
"tsconfig-paths": "^4.2.0",
|
39
|
-
"typescript": "^5.7.2"
|
41
|
+
"typescript": "^5.7.2",
|
42
|
+
"meilisearch": "^0.37.0",
|
43
|
+
"redis": "^4.6.13",
|
44
|
+
"dotenv": "^16.4.5"
|
40
45
|
},
|
41
46
|
"repository": {
|
42
47
|
"type": "git",
|
package/services/agenda.ts
CHANGED
@@ -1,25 +1,9 @@
|
|
1
|
+
import { ScheduledRequest } from "@/types";
|
1
2
|
import cron from "node-cron";
|
2
|
-
import { Orchestrator } from "../llm/orchestrator";
|
3
|
-
import { RedisCache } from "./cache";
|
4
|
-
|
5
|
-
interface ScheduledRequest {
|
6
|
-
id: string;
|
7
|
-
originalRequest: string;
|
8
|
-
cronExpression: string;
|
9
|
-
isRecurring: boolean;
|
10
|
-
createdAt: Date;
|
11
|
-
}
|
12
3
|
|
13
4
|
export class Agenda {
|
14
5
|
private scheduledRequests: Map<string, ScheduledRequest> = new Map();
|
15
6
|
private cronJobs: Map<string, cron.ScheduledTask> = new Map();
|
16
|
-
private readonly orchestrator: Orchestrator;
|
17
|
-
private readonly cache: RedisCache;
|
18
|
-
|
19
|
-
constructor(orchestrator: Orchestrator, cache: RedisCache) {
|
20
|
-
this.orchestrator = orchestrator;
|
21
|
-
this.cache = cache;
|
22
|
-
}
|
23
7
|
|
24
8
|
/**
|
25
9
|
* Schedule a new request to be processed later
|
@@ -45,39 +29,34 @@ export class Agenda {
|
|
45
29
|
};
|
46
30
|
|
47
31
|
// Create cron job
|
48
|
-
const cronJob = cron.schedule(request.cronExpression,
|
49
|
-
|
32
|
+
const cronJob = cron.schedule(request.cronExpression, () => {
|
33
|
+
console.log(`🔄 Executing scheduled request: ${id}`);
|
50
34
|
|
51
|
-
if (callbacks?.onExecuted)
|
35
|
+
if (callbacks?.onExecuted) {
|
52
36
|
callbacks.onExecuted(id, scheduledRequest.originalRequest);
|
37
|
+
}
|
38
|
+
|
39
|
+
console.log(`✅ Scheduled request executed successfully: ${id}`);
|
53
40
|
|
41
|
+
// Auto-stop pour les tâches non récurrentes
|
54
42
|
if (!scheduledRequest.isRecurring) {
|
55
43
|
this.cancelScheduledRequest(id);
|
56
44
|
}
|
57
45
|
});
|
58
46
|
|
47
|
+
// Démarrer le job en mode non-running
|
48
|
+
cronJob.stop();
|
49
|
+
|
59
50
|
// Store request and job
|
60
51
|
this.scheduledRequests.set(id, scheduledRequest);
|
61
52
|
this.cronJobs.set(id, cronJob);
|
62
53
|
|
63
54
|
if (callbacks?.onScheduled) callbacks.onScheduled(id);
|
64
55
|
|
65
|
-
|
66
|
-
|
56
|
+
// Démarrer le job après l'avoir stocké
|
57
|
+
cronJob.start();
|
67
58
|
|
68
|
-
|
69
|
-
* Execute a scheduled request by launching a new process
|
70
|
-
*/
|
71
|
-
private async executeScheduledRequest(
|
72
|
-
request: ScheduledRequest
|
73
|
-
): Promise<void> {
|
74
|
-
try {
|
75
|
-
console.log(`🔄 Executing scheduled request from ${request.createdAt}`);
|
76
|
-
|
77
|
-
console.log(`✅ Scheduled request executed successfully`);
|
78
|
-
} catch (error) {
|
79
|
-
console.error(`❌ Failed to execute scheduled request:`, error);
|
80
|
-
}
|
59
|
+
return id;
|
81
60
|
}
|
82
61
|
|
83
62
|
/**
|
@@ -86,10 +65,17 @@ export class Agenda {
|
|
86
65
|
cancelScheduledRequest(requestId: string): boolean {
|
87
66
|
const cronJob = this.cronJobs.get(requestId);
|
88
67
|
if (cronJob) {
|
89
|
-
|
90
|
-
|
68
|
+
try {
|
69
|
+
cronJob.stop();
|
70
|
+
this.cronJobs.delete(requestId);
|
71
|
+
this.scheduledRequests.delete(requestId);
|
72
|
+
return true;
|
73
|
+
} catch (error) {
|
74
|
+
console.error(`Failed to stop cron job ${requestId}:`, error);
|
75
|
+
return false;
|
76
|
+
}
|
91
77
|
}
|
92
|
-
return
|
78
|
+
return false;
|
93
79
|
}
|
94
80
|
|
95
81
|
/**
|
@@ -103,11 +89,30 @@ export class Agenda {
|
|
103
89
|
* Stop all cron jobs
|
104
90
|
*/
|
105
91
|
stopAll(): void {
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
92
|
+
const ids = Array.from(this.cronJobs.keys());
|
93
|
+
|
94
|
+
// Arrêter tous les jobs de manière synchrone
|
95
|
+
for (const id of ids) {
|
96
|
+
const job = this.cronJobs.get(id);
|
97
|
+
if (job) {
|
98
|
+
job.stop();
|
99
|
+
this.cronJobs.delete(id);
|
100
|
+
this.scheduledRequests.delete(id);
|
101
|
+
}
|
110
102
|
}
|
111
|
-
|
103
|
+
|
104
|
+
// Double vérification
|
105
|
+
this.cronJobs.clear();
|
106
|
+
this.scheduledRequests.clear();
|
107
|
+
}
|
108
|
+
|
109
|
+
public async stop(): Promise<void> {
|
110
|
+
this.stopAll();
|
111
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
112
|
+
}
|
113
|
+
|
114
|
+
public async cancel(query: {}): Promise<void> {
|
115
|
+
this.stopAll();
|
116
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
112
117
|
}
|
113
118
|
}
|