@ai.ntellect/core 0.3.0 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
package/dist/types.d.ts CHANGED
@@ -154,8 +154,7 @@ export interface Memory {
154
154
  query: string;
155
155
  purpose: string;
156
156
  data: any;
157
- scope: MemoryScopeType;
158
- userId?: string;
157
+ roomId: string;
159
158
  createdAt: Date;
160
159
  chunks?: MemoryChunk[];
161
160
  }
@@ -205,3 +204,24 @@ export interface TransformedQueueItem {
205
204
  name: string;
206
205
  parameters: QueueItemParameter[];
207
206
  }
207
+ export interface ScheduledAction {
208
+ id: string;
209
+ action: {
210
+ name: string;
211
+ parameters: QueueItemParameter[];
212
+ };
213
+ scheduledTime: Date;
214
+ userId: string;
215
+ status: "pending" | "completed" | "failed";
216
+ recurrence?: {
217
+ type: "daily" | "weekly" | "monthly";
218
+ interval: number;
219
+ };
220
+ }
221
+ export interface ScheduledActionEvents {
222
+ onActionStart?: (action: ScheduledAction) => void;
223
+ onActionComplete?: (action: ScheduledAction, result: any) => void;
224
+ onActionFailed?: (action: ScheduledAction, error: Error) => void;
225
+ onActionScheduled?: (action: ScheduledAction) => void;
226
+ onActionCancelled?: (actionId: string) => void;
227
+ }
@@ -1,13 +1,15 @@
1
1
  export const evaluatorContext = {
2
2
  behavior: {
3
- language: "user_language",
4
- role: "Your role is to verify if the goal has been achieved and make a response or suggest next actions.",
3
+ language: "same_as_user",
4
+ role: "Your role is to ensure the goal will be achieved and make a response or suggest next actions.",
5
5
  guidelines: {
6
6
  important: [
7
- "Verify if all required actions were executed successfully.",
8
- "Check if the results align with the initial goal.",
7
+ "Verify if all actions were executed successfully (actionsAlreadyDone).",
8
+ "Check if the results align with the initial goal (explain in 'why' field).",
9
+ "Suggest next actions in 'nextActionsNeeded' if the goal is not achieved and if actions in 'actionsAlreadyDone' are not enough.",
9
10
  "If you retrieved the informations from your internal knowledge base, no need to store them in 'extraInformationsToStore'.",
10
- "Store ONLY extra new needed informations in 'extraInformationsToStore' (choose the most relevant informations and memory type: episodic, semantic, or procedural).",
11
+ "Store ONLY new needed informations in 'extraInformationsToStore'.",
12
+ "Choose the most relevant informations and memory type: episodic, semantic, or procedural.",
11
13
  ],
12
14
  warnings: [
13
15
  "NEVER store an old data you retrieve from your internal knowledge base.",
@@ -3,7 +3,7 @@ import { generateObject } from "ai";
3
3
  import { z } from "zod";
4
4
  import { CacheMemory } from "../../memory/cache";
5
5
  import { PersistentMemory } from "../../memory/persistent";
6
- import { ActionSchema, MemoryScope, MemoryType, State } from "../../types";
6
+ import { ActionSchema, MemoryType, State } from "../../types";
7
7
  import { injectActions } from "../../utils/inject-actions";
8
8
  import { Interpreter } from "../interpreter";
9
9
  import { evaluatorContext } from "./context";
@@ -62,8 +62,8 @@ export class Evaluator {
62
62
  const response = await generateObject({
63
63
  model: this.model,
64
64
  schema: z.object({
65
- actionsCompleted: z.array(z.string()),
66
- actionsFailed: z.array(z.string()),
65
+ requestLanguage: z.string(),
66
+ actionsAlreadyDone: z.array(z.string()),
67
67
  extraInformationsToStore: z.array(
68
68
  z.object({
69
69
  memoryType: z.enum(["episodic", "semantic", "procedural"]),
@@ -111,7 +111,7 @@ export class Evaluator {
111
111
  console.log("Type:", item.memoryType);
112
112
  console.log("Content:", item.queryForData);
113
113
 
114
- const memories = await this.memory.persistent.searchSimilarQueries(
114
+ const memories = await this.memory.persistent.findRelevantDocuments(
115
115
  item.queryForData,
116
116
  {
117
117
  similarityThreshold: 70,
@@ -129,7 +129,7 @@ export class Evaluator {
129
129
  purpose: item.memoryType,
130
130
  query: item.queryForData,
131
131
  data: item.data,
132
- scope: MemoryScope.GLOBAL,
132
+ roomId: "global",
133
133
  createdAt: new Date(),
134
134
  });
135
135
  }
@@ -141,13 +141,12 @@ export class Evaluator {
141
141
  cacheMemory.createMemory({
142
142
  content: prompt,
143
143
  type: MemoryType.ACTION,
144
- data: validatedResponse.actionsCompleted,
145
- scope: MemoryScope.GLOBAL,
144
+ data: validatedResponse.actionsAlreadyDone,
146
145
  });
147
146
  console.log(
148
147
  "✅ Workflow actions completed stored in cache",
149
148
  prompt,
150
- validatedResponse.actionsCompleted
149
+ validatedResponse.actionsAlreadyDone
151
150
  );
152
151
  }
153
152
  console.log("\n✅ Evaluation completed");
@@ -164,7 +163,7 @@ export class Evaluator {
164
163
  if (error.value.extraInformationsToStore.length > 0) {
165
164
  for (const item of error.value.extraInformationsToStore) {
166
165
  // Check if the item is already in the memory
167
- const memories = await this.memory.persistent.searchSimilarQueries(
166
+ const memories = await this.memory.persistent.findRelevantDocuments(
168
167
  item.content
169
168
  );
170
169
  if (memories.length === 0) {
@@ -177,7 +176,7 @@ export class Evaluator {
177
176
  purpose: "importantToRemember",
178
177
  query: item.content,
179
178
  data: item.data,
180
- scope: MemoryScope.USER,
179
+ roomId: "global",
181
180
  createdAt: new Date(),
182
181
  });
183
182
  }
@@ -1,6 +1,6 @@
1
1
  export const generalInterpreterContext = {
2
2
  role: "You are the general assistant. Your role is to provide a clear and factual analysis of the results.",
3
- language: "user_language",
3
+ language: "same_as_user",
4
4
  guidelines: {
5
5
  important: [],
6
6
  warnings: [],
@@ -1,17 +1,15 @@
1
1
  export const orchestratorContext = {
2
2
  behavior: {
3
- language: "user_language",
4
- role: "You are the orchestrator agent. Your role is to determine what actions are needed to achieve the user goal.",
3
+ language: "same_as_user",
4
+ role: "Your role is to determine what actions are needed to achieve the user goal.",
5
5
  guidelines: {
6
6
  important: [
7
7
  "If there is no action to do, you must answer in the 'answer' field.",
8
8
  "If some parameters are not clear or missing, don't add the action, YOU MUST ask the user for them.",
9
- "ALWAYS use the same language as user request. (If it's English, use English, if it's French, use French, etc.)",
9
+ "For QUESTIONS or ANALYSIS, search first in your internal knowledge base before using actions.",
10
10
  "For ON-CHAIN actions, just use the useful actions.",
11
- "For QUESTIONS or ANALYSIS, you MUST search in your cache memory or/and internal knowledge base.",
12
- "NEVER repeat same actions if the user doesn't ask for it.",
13
11
  ],
14
- warnings: [],
12
+ warnings: ["NEVER repeat same actions if the user doesn't ask for it."],
15
13
  },
16
14
  },
17
15
  };
@@ -41,7 +41,7 @@ export class Orchestrator {
41
41
  }),
42
42
  execute: async ({ query }: { query: string }) => {
43
43
  const persistentMemories =
44
- await this.memory.persistent.searchSimilarQueries(query, {
44
+ await this.memory.persistent.findRelevantDocuments(query, {
45
45
  similarityThreshold: 70,
46
46
  });
47
47
  return `# LONG_TERM_MEMORY: ${JSON.stringify(persistentMemories)}`;
package/memory/cache.ts CHANGED
@@ -44,15 +44,8 @@ export class CacheMemory {
44
44
  }
45
45
  }
46
46
 
47
- private getMemoryKey(scope: MemoryScope, userId?: string): string {
48
- if (scope === MemoryScope.GLOBAL) {
49
- return `${this.CACHE_PREFIX}global:`;
50
- }
51
- return `${this.CACHE_PREFIX}user:${userId}:`;
52
- }
53
-
54
47
  private async storeMemory(memory: CacheMemoryType) {
55
- const prefix = this.getMemoryKey(memory.scope, memory.userId);
48
+ const prefix = this.CACHE_PREFIX;
56
49
  const key = `${prefix}${memory.id}`;
57
50
  const result = await this.redis.set(key, JSON.stringify(memory), {
58
51
  EX: this.CACHE_TTL,
@@ -124,23 +117,10 @@ export class CacheMemory {
124
117
  scope?: MemoryScope,
125
118
  userId?: string
126
119
  ): Promise<CacheMemoryType[]> {
127
- let patterns: CacheMemoryType[] = [];
128
-
129
- if (!scope || scope === MemoryScope.GLOBAL) {
130
- const globalPrefix = this.getMemoryKey(MemoryScope.GLOBAL);
131
- const globalKeys = await this.redis.keys(`${globalPrefix}*`);
132
- const globalPatterns = await this.getMemoriesFromKeys(globalKeys);
133
- patterns = patterns.concat(globalPatterns);
134
- }
135
-
136
- if (userId && (!scope || scope === MemoryScope.USER)) {
137
- const userPrefix = this.getMemoryKey(MemoryScope.USER, userId);
138
- const userKeys = await this.redis.keys(`${userPrefix}*`);
139
- const userPatterns = await this.getMemoriesFromKeys(userKeys);
140
- patterns = patterns.concat(userPatterns);
141
- }
120
+ const keys = await this.redis.keys(`${this.CACHE_PREFIX}*`);
121
+ const memories = await this.getMemoriesFromKeys(keys);
142
122
 
143
- return patterns;
123
+ return memories;
144
124
  }
145
125
 
146
126
  private async getMemoriesFromKeys(
@@ -28,18 +28,6 @@ interface MeilisearchResponse {
28
28
  }>;
29
29
  }
30
30
 
31
- interface SearchParams {
32
- q?: string;
33
- offset?: number;
34
- limit?: number;
35
- filter?: string | string[];
36
- facets?: string[];
37
- attributesToRetrieve?: string[];
38
- attributesToSearchOn?: string[];
39
- sort?: string[];
40
- matchingStrategy?: "last" | "all" | "frequency";
41
- }
42
-
43
31
  interface ProcessedChunk {
44
32
  content: string;
45
33
  embedding: number[];
@@ -56,7 +44,7 @@ export class PersistentMemory {
56
44
  constructor(options: { host: string; apiKey: string; indexPrefix?: string }) {
57
45
  this.host = options.host;
58
46
  this.apiKey = options.apiKey;
59
- this.INDEX_PREFIX = options.indexPrefix || "memory_";
47
+ this.INDEX_PREFIX = options.indexPrefix || "memory";
60
48
  }
61
49
 
62
50
  /**
@@ -64,10 +52,7 @@ export class PersistentMemory {
64
52
  */
65
53
  async init() {
66
54
  // Create global index
67
- await this._getOrCreateIndex(this._getIndexName(MemoryScope.GLOBAL));
68
-
69
- // Create user index
70
- await this._getOrCreateIndex(this._getIndexName(MemoryScope.USER));
55
+ await this._getOrCreateIndex(this.INDEX_PREFIX);
71
56
  }
72
57
 
73
58
  /**
@@ -94,17 +79,6 @@ export class PersistentMemory {
94
79
 
95
80
  return response.json() as Promise<T>;
96
81
  }
97
-
98
- /**
99
- * Get index name based on scope and userId
100
- */
101
- private _getIndexName(scope: MemoryScope, userId?: string): string {
102
- if (scope === "global") {
103
- return `${this.INDEX_PREFIX}global`;
104
- }
105
- return `${this.INDEX_PREFIX}user_${userId}`;
106
- }
107
-
108
82
  /**
109
83
  * Get or create an index with proper settings
110
84
  */
@@ -161,8 +135,7 @@ export class PersistentMemory {
161
135
  * Store a memory in the database
162
136
  */
163
137
  async createMemory(memory: Memory) {
164
- const indexName = this._getIndexName(memory.scope, memory.userId);
165
- await this._getOrCreateIndex(indexName);
138
+ await this._getOrCreateIndex(memory.roomId);
166
139
 
167
140
  const chunks = await this.processContent(memory.data);
168
141
 
@@ -173,7 +146,7 @@ export class PersistentMemory {
173
146
  };
174
147
 
175
148
  const response = await this._makeRequest(
176
- `/indexes/${indexName}/documents`,
149
+ `/indexes/${this.INDEX_PREFIX}/documents`,
177
150
  {
178
151
  method: "POST",
179
152
  body: JSON.stringify([document]),
@@ -186,7 +159,7 @@ export class PersistentMemory {
186
159
  /**
187
160
  * Find best matching memories
188
161
  */
189
- async searchSimilarQueries(query: string, options: SearchOptions = {}) {
162
+ async findRelevantDocuments(query: string, options: SearchOptions = {}) {
190
163
  console.log("\n🔍 Searching in persistent memory");
191
164
  console.log("Query:", query);
192
165
  console.log("Options:", JSON.stringify(options, null, 2));
@@ -199,42 +172,20 @@ export class PersistentMemory {
199
172
 
200
173
  const searchResults = [];
201
174
 
202
- // Search in global memories
203
- if (!options.scope || options.scope === "global") {
204
- const globalIndex = this._getIndexName(MemoryScope.GLOBAL);
205
- console.log("\n📚 Searching in global index:", globalIndex);
206
- try {
207
- const globalResults = await this._makeRequest<MeilisearchResponse>(
208
- `/indexes/${globalIndex}/search`,
209
- {
210
- method: "POST",
211
- body: JSON.stringify({ q: query }),
212
- }
213
- );
214
- if (globalResults?.hits) {
215
- searchResults.push(...globalResults.hits);
216
- }
217
- } catch (error) {
218
- console.error("❌ Error searching global index:", error);
219
- }
220
- }
221
-
222
- // Search in user memories
223
- if (
224
- options.userId &&
225
- (!options.scope || options.scope === MemoryScope.USER)
226
- ) {
227
- const userIndex = this._getIndexName(MemoryScope.USER, options.userId);
228
- const userResults = await this._makeRequest<MeilisearchResponse>(
229
- `/indexes/${userIndex}/search`,
175
+ console.log("\n📚 Searching in global index:", this.INDEX_PREFIX);
176
+ try {
177
+ const globalResults = await this._makeRequest<MeilisearchResponse>(
178
+ `/indexes/${this.INDEX_PREFIX}/search`,
230
179
  {
231
180
  method: "POST",
232
181
  body: JSON.stringify({ q: query }),
233
182
  }
234
183
  );
235
- if (userResults.hits) {
236
- searchResults.push(...userResults.hits);
184
+ if (globalResults?.hits) {
185
+ searchResults.push(...globalResults.hits);
237
186
  }
187
+ } catch (error) {
188
+ console.error("❌ Error searching global index:", error);
238
189
  }
239
190
 
240
191
  const totalResults = searchResults.length;
@@ -244,13 +195,11 @@ export class PersistentMemory {
244
195
  const results = searchResults
245
196
  .flatMap((hit) => {
246
197
  const chunkSimilarities = hit.chunks.map((chunk) => ({
247
- createdAt: hit.createdAt,
248
- data: hit.data,
249
- purpose: hit.purpose,
250
198
  query: hit.query,
251
- chunk: chunk.content,
199
+ data: hit.data,
252
200
  similarityPercentage:
253
201
  (cosineSimilarity(queryEmbedding, chunk.embedding) + 1) * 50,
202
+ createdAt: hit.createdAt,
254
203
  }));
255
204
 
256
205
  return chunkSimilarities.reduce(
@@ -275,10 +224,6 @@ export class PersistentMemory {
275
224
  results.forEach((match, index) => {
276
225
  console.log(`\n${index + 1}. Match Details:`);
277
226
  console.log(` Query: ${match.query}`);
278
- console.log(` Purpose: ${match.purpose}`);
279
- console.log(` Similarity: ${match.similarityPercentage.toFixed(2)}%`);
280
- console.log(` Content: "${match.chunk}"`);
281
- console.log("─".repeat(50));
282
227
  });
283
228
  } else {
284
229
  console.log("\n❌ No relevant matches found");
@@ -290,9 +235,8 @@ export class PersistentMemory {
290
235
  /**
291
236
  * Delete memories for a given scope and user
292
237
  */
293
- async deleteMemories(scope: MemoryScope, userId?: string) {
294
- const indexName = this._getIndexName(scope, userId);
295
- return this._makeRequest(`/indexes/${indexName}`, {
238
+ async deleteMemories() {
239
+ return this._makeRequest(`/indexes/${this.INDEX_PREFIX}`, {
296
240
  method: "DELETE",
297
241
  });
298
242
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai.ntellect/core",
3
- "version": "0.3.0",
3
+ "version": "0.3.3",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -0,0 +1,167 @@
1
+ import { Orchestrator } from "../llm/orchestrator";
2
+ import { ActionSchema, ScheduledAction, ScheduledActionEvents } from "../types";
3
+ import { ActionQueueManager } from "./queue";
4
+
5
+ export class ActionScheduler {
6
+ private scheduledActions: Map<string, NodeJS.Timeout> = new Map();
7
+ private storage: ScheduledActionStorage;
8
+ private events: ScheduledActionEvents;
9
+
10
+ constructor(
11
+ private actionQueueManager: ActionQueueManager,
12
+ private orchestrator: Orchestrator,
13
+ events: ScheduledActionEvents = {}
14
+ ) {
15
+ this.storage = new ScheduledActionStorage();
16
+ this.events = events;
17
+ this.initializeScheduledActions();
18
+ }
19
+
20
+ async scheduleAction(
21
+ action: ActionSchema,
22
+ scheduledTime: Date,
23
+ userId: string,
24
+ recurrence?: ScheduledAction["recurrence"]
25
+ ): Promise<string> {
26
+ const scheduledAction: ScheduledAction = {
27
+ id: crypto.randomUUID(),
28
+ action: {
29
+ name: action.name,
30
+ parameters: [],
31
+ },
32
+ scheduledTime,
33
+ userId,
34
+ status: "pending",
35
+ recurrence,
36
+ };
37
+
38
+ await this.storage.saveScheduledAction(scheduledAction);
39
+ this.scheduleExecution(scheduledAction);
40
+ this.events.onActionScheduled?.(scheduledAction);
41
+
42
+ return scheduledAction.id;
43
+ }
44
+
45
+ private async initializeScheduledActions() {
46
+ const pendingActions = await this.storage.getPendingActions();
47
+ pendingActions.forEach((action) => this.scheduleExecution(action));
48
+ }
49
+
50
+ private scheduleExecution(scheduledAction: ScheduledAction) {
51
+ const now = new Date();
52
+ const delay = scheduledAction.scheduledTime.getTime() - now.getTime();
53
+
54
+ if (delay < 0) return;
55
+
56
+ const timeout = setTimeout(async () => {
57
+ try {
58
+ await this.executeScheduledAction(scheduledAction);
59
+
60
+ if (scheduledAction.recurrence) {
61
+ const nextExecutionTime = this.calculateNextExecutionTime(
62
+ scheduledAction.scheduledTime,
63
+ scheduledAction.recurrence
64
+ );
65
+ const actionSchema = this.orchestrator.tools.find(
66
+ (tool: ActionSchema) => tool.name === scheduledAction.action.name
67
+ );
68
+ if (actionSchema) {
69
+ await this.scheduleAction(
70
+ actionSchema,
71
+ nextExecutionTime,
72
+ scheduledAction.userId,
73
+ scheduledAction.recurrence
74
+ );
75
+ }
76
+ }
77
+ } catch (error) {
78
+ console.error(
79
+ `Failed to execute scheduled action ${scheduledAction.id}:`,
80
+ error
81
+ );
82
+ await this.storage.updateActionStatus(scheduledAction.id, "failed");
83
+ }
84
+ }, delay);
85
+
86
+ this.scheduledActions.set(scheduledAction.id, timeout);
87
+ }
88
+
89
+ private async executeScheduledAction(scheduledAction: ScheduledAction) {
90
+ try {
91
+ this.events.onActionStart?.(scheduledAction);
92
+
93
+ this.actionQueueManager.addToQueue({
94
+ name: scheduledAction.action.name,
95
+ parameters: scheduledAction.action.parameters,
96
+ });
97
+
98
+ const result = await this.actionQueueManager.processQueue();
99
+ await this.storage.updateActionStatus(scheduledAction.id, "completed");
100
+
101
+ this.events.onActionComplete?.(scheduledAction, result);
102
+ } catch (error) {
103
+ await this.storage.updateActionStatus(scheduledAction.id, "failed");
104
+ this.events.onActionFailed?.(scheduledAction, error as Error);
105
+ throw error;
106
+ }
107
+ }
108
+
109
+ private calculateNextExecutionTime(
110
+ currentTime: Date,
111
+ recurrence: NonNullable<ScheduledAction["recurrence"]>
112
+ ): Date {
113
+ const nextTime = new Date(currentTime);
114
+
115
+ switch (recurrence.type) {
116
+ case "daily":
117
+ nextTime.setDate(nextTime.getDate() + recurrence.interval);
118
+ break;
119
+ case "weekly":
120
+ nextTime.setDate(nextTime.getDate() + 7 * recurrence.interval);
121
+ break;
122
+ case "monthly":
123
+ nextTime.setMonth(nextTime.getMonth() + recurrence.interval);
124
+ break;
125
+ }
126
+
127
+ return nextTime;
128
+ }
129
+
130
+ async cancelScheduledAction(actionId: string): Promise<boolean> {
131
+ const timeout = this.scheduledActions.get(actionId);
132
+ if (timeout) {
133
+ clearTimeout(timeout);
134
+ this.scheduledActions.delete(actionId);
135
+ await this.storage.deleteScheduledAction(actionId);
136
+ this.events.onActionCancelled?.(actionId);
137
+ return true;
138
+ }
139
+ return false;
140
+ }
141
+ }
142
+
143
+ class ScheduledActionStorage {
144
+ private actions: ScheduledAction[] = [];
145
+
146
+ async saveScheduledAction(action: ScheduledAction): Promise<void> {
147
+ this.actions.push(action);
148
+ }
149
+
150
+ async getPendingActions(): Promise<ScheduledAction[]> {
151
+ return this.actions.filter((action) => action.status === "pending");
152
+ }
153
+
154
+ async updateActionStatus(
155
+ actionId: string,
156
+ status: ScheduledAction["status"]
157
+ ): Promise<void> {
158
+ const action = this.actions.find((a) => a.id === actionId);
159
+ if (action) {
160
+ action.status = status;
161
+ }
162
+ }
163
+
164
+ async deleteScheduledAction(actionId: string): Promise<void> {
165
+ this.actions = this.actions.filter((a) => a.id !== actionId);
166
+ }
167
+ }