@ai.ntellect/core 0.8.1 → 0.8.3

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.
Files changed (47) hide show
  1. package/README.md +190 -118
  2. package/dist/graph/controller.d.ts +4 -5
  3. package/dist/graph/controller.d.ts.map +1 -1
  4. package/dist/graph/controller.js +10 -10
  5. package/dist/graph/controller.js.map +1 -1
  6. package/dist/graph/event-manager.d.ts.map +1 -1
  7. package/dist/graph/event-manager.js +4 -4
  8. package/dist/graph/event-manager.js.map +1 -1
  9. package/dist/graph/index.d.ts +5 -16
  10. package/dist/graph/index.d.ts.map +1 -1
  11. package/dist/graph/index.js +32 -52
  12. package/dist/graph/index.js.map +1 -1
  13. package/dist/graph/node.d.ts +1 -10
  14. package/dist/graph/node.d.ts.map +1 -1
  15. package/dist/graph/node.js +7 -36
  16. package/dist/graph/node.js.map +1 -1
  17. package/dist/graph/observer.d.ts +4 -0
  18. package/dist/graph/observer.d.ts.map +1 -1
  19. package/dist/graph/observer.js +27 -1
  20. package/dist/graph/observer.js.map +1 -1
  21. package/dist/modules/agent/agent.d.ts.map +1 -1
  22. package/dist/modules/agent/agent.js +3 -6
  23. package/dist/modules/agent/agent.js.map +1 -1
  24. package/dist/modules/agent/generic-assistant.d.ts.map +1 -1
  25. package/dist/modules/agent/generic-assistant.js +3 -3
  26. package/dist/modules/agent/generic-assistant.js.map +1 -1
  27. package/dist/modules/agent/generic-executor.d.ts +1 -0
  28. package/dist/modules/agent/generic-executor.d.ts.map +1 -1
  29. package/dist/modules/agent/generic-executor.js +24 -35
  30. package/dist/modules/agent/generic-executor.js.map +1 -1
  31. package/dist/modules/agent/llm-factory.d.ts.map +1 -1
  32. package/dist/types/index.d.ts +90 -25
  33. package/dist/types/index.d.ts.map +1 -1
  34. package/graph/controller.ts +10 -11
  35. package/graph/event-manager.ts +2 -14
  36. package/graph/index.ts +37 -67
  37. package/graph/node.ts +5 -39
  38. package/graph/observer.ts +35 -5
  39. package/modules/agent/agent.ts +7 -8
  40. package/modules/agent/generic-assistant.ts +7 -5
  41. package/modules/agent/generic-executor.ts +33 -40
  42. package/package.json +1 -1
  43. package/test/graph/controller.test.ts +62 -17
  44. package/test/graph/index.test.ts +128 -133
  45. package/test/graph/node.test.ts +38 -233
  46. package/test/graph/observer.test.ts +18 -23
  47. package/types/index.ts +91 -28
package/graph/node.ts CHANGED
@@ -86,7 +86,6 @@ export class GraphNode<T extends ZodSchema> {
86
86
  public async executeNode(
87
87
  nodeName: string,
88
88
  context: GraphContext<T>,
89
- params: any,
90
89
  triggeredByEvent: boolean = false
91
90
  ): Promise<void> {
92
91
  const node = this.nodes.get(nodeName);
@@ -120,11 +119,11 @@ export class GraphNode<T extends ZodSchema> {
120
119
  },
121
120
  });
122
121
 
123
- if (node.condition && !node.condition(contextProxy, params)) {
122
+ if (node.condition && !node.condition(contextProxy)) {
124
123
  return;
125
124
  }
126
125
 
127
- await this.executeWithRetry(node, contextProxy, nodeName, params);
126
+ await this.executeWithRetry(node, contextProxy, nodeName);
128
127
  this.emitEvent("nodeCompleted", { name: nodeName, context: nodeContext });
129
128
 
130
129
  if (!triggeredByEvent && node.next) {
@@ -151,7 +150,7 @@ export class GraphNode<T extends ZodSchema> {
151
150
 
152
151
  // Sinon, exécuter les autres nœuds valides
153
152
  for (const nextNode of validNextNodes) {
154
- await this.executeNode(nextNode.name, context, undefined, false);
153
+ await this.executeNode(nextNode.name, context, false);
155
154
  }
156
155
  }
157
156
  } catch (error) {
@@ -164,34 +163,6 @@ export class GraphNode<T extends ZodSchema> {
164
163
  }
165
164
  }
166
165
 
167
- /**
168
- * Validates the params for a node using its schema
169
- * @param node - The node whose params need validation
170
- * @param params - The input data to validate
171
- * @param nodeName - The name of the node (for error messages)
172
- * @throws Error if validation fails
173
- * @private
174
- */
175
- private async validateParams(
176
- node: GraphNodeConfig<T, any>,
177
- params: any,
178
- nodeName: string
179
- ): Promise<void> {
180
- // Si pas de schéma de validation ou si le schéma est optionnel, accepter n'importe quels params
181
- if (!node.params || node.params.isOptional?.()) return;
182
-
183
- // Vérifier les params uniquement si un schéma est défini et non optionnel
184
- if (!params) {
185
- throw new Error(`Params required for node "${nodeName}"`);
186
- }
187
-
188
- try {
189
- return node.params.parse(params);
190
- } catch (error: any) {
191
- throw error;
192
- }
193
- }
194
-
195
166
  /**
196
167
  * Executes a node with retry logic
197
168
  * @param node - The node to execute
@@ -205,19 +176,14 @@ export class GraphNode<T extends ZodSchema> {
205
176
  private async executeWithRetry(
206
177
  node: GraphNodeConfig<T, any>,
207
178
  contextProxy: GraphContext<T>,
208
- nodeName: string,
209
- params?: NodeParams
179
+ nodeName: string
210
180
  ): Promise<void> {
211
181
  let attempts = 0;
212
182
  let lastError: Error = new Error("Unknown error");
213
183
 
214
184
  while (attempts < (node.retry?.maxAttempts || 1)) {
215
185
  try {
216
- if (node.params) {
217
- await this.validateParams(node, params, nodeName);
218
- }
219
-
220
- await node.execute(contextProxy, params, {
186
+ await node.execute(contextProxy, {
221
187
  eventEmitter: this.eventEmitter,
222
188
  });
223
189
  return;
package/graph/observer.ts CHANGED
@@ -125,17 +125,47 @@ export class GraphObserver<T extends ZodSchema> {
125
125
 
126
126
  /**
127
127
  * Waits for correlated events to occur and validates them using a correlation function
128
+ * @param eventTypes - Array of event types to wait for
129
+ * @param timeoutMs - Timeout in milliseconds
130
+ * @param correlationFn - Function to validate event correlation
131
+ * @returns Promise that resolves with the correlated events
128
132
  */
129
133
  waitForCorrelatedEvents(
130
134
  eventTypes: string[],
131
135
  timeoutMs: number,
132
136
  correlationFn: (events: GraphEvent<T>[]) => boolean
133
137
  ): Promise<GraphEvent<T>[]> {
134
- return this.eventManager.waitForCorrelatedEvents(
135
- eventTypes,
136
- timeoutMs,
137
- correlationFn
138
- );
138
+ return new Promise((resolve, reject) => {
139
+ const events: GraphEvent<T>[] = [];
140
+ const timeout = setTimeout(() => {
141
+ reject(
142
+ new Error(
143
+ `Timeout waiting for correlated events: ${eventTypes.join(", ")}`
144
+ )
145
+ );
146
+ }, timeoutMs);
147
+
148
+ const subscription = this.eventSubject
149
+ .pipe(
150
+ filter((event) => eventTypes.includes(event.type)),
151
+ takeUntil(this.destroySubject)
152
+ )
153
+ .subscribe({
154
+ next: (event) => {
155
+ events.push(event);
156
+ if (events.length === eventTypes.length && correlationFn(events)) {
157
+ clearTimeout(timeout);
158
+ subscription.unsubscribe();
159
+ resolve(events);
160
+ }
161
+ },
162
+ error: (error) => {
163
+ clearTimeout(timeout);
164
+ subscription.unsubscribe();
165
+ reject(error);
166
+ },
167
+ });
168
+ });
139
169
  }
140
170
 
141
171
  /**
@@ -1,3 +1,4 @@
1
+ import { GraphContext } from "@/types";
1
2
  import { GraphFlow } from "../../graph/index";
2
3
  import {
3
4
  AgentConfig,
@@ -51,31 +52,29 @@ export class Agent {
51
52
  * @returns {GraphFlow<typeof AgentContextSchema>} The configured workflow
52
53
  */
53
54
  private setupWorkflow(): GraphFlow<typeof AgentContextSchema> {
54
- return new GraphFlow("assistant", {
55
+ return new GraphFlow({
55
56
  name: "assistant",
56
57
  schema: AgentContextSchema,
57
58
  context: {
58
59
  input: { raw: "" },
59
60
  actions: [],
60
61
  response: "",
61
- executedActions: [],
62
62
  },
63
63
  nodes: [
64
64
  {
65
65
  name: "process",
66
- execute: async (context) => {
66
+ execute: async (context: GraphContext<typeof AgentContextSchema>) => {
67
67
  const agentContext = context as unknown as AgentContext;
68
68
  const decision = await this.executor.makeDecision(agentContext);
69
69
  context.actions = decision.actions;
70
70
  context.response = decision.response;
71
71
  },
72
- next: (context) => (context.actions.length > 0 ? ["execute"] : []),
72
+ next: (context: GraphContext<typeof AgentContextSchema>) =>
73
+ context.actions.length > 0 ? ["execute"] : [],
73
74
  },
74
75
  {
75
76
  name: "execute",
76
- execute: async (context) => {
77
- console.log(`Executing actions:`);
78
- console.log(context.actions);
77
+ execute: async (context: GraphContext<typeof AgentContextSchema>) => {
79
78
  const timestamp = new Date().toISOString();
80
79
  context.knowledge = `Date: ${timestamp}\n${JSON.stringify(
81
80
  context.actions
@@ -97,7 +96,7 @@ export class Agent {
97
96
  * @returns {Promise<AgentContext>} The resulting context after processing
98
97
  */
99
98
  public async process(input: string): Promise<AgentContext> {
100
- await this.workflow.execute("process", undefined, {
99
+ await this.workflow.execute("process", {
101
100
  input: { raw: input },
102
101
  actions: [],
103
102
  response: "",
@@ -1,3 +1,4 @@
1
+ import { GraphContext } from "@/types";
1
2
  import { GraphFlow } from "../../graph/index";
2
3
  import {
3
4
  AgentConfig,
@@ -41,7 +42,7 @@ export class Agent {
41
42
  }
42
43
 
43
44
  private setupWorkflow(): GraphFlow<typeof AgentContextSchema> {
44
- return new GraphFlow("assistant", {
45
+ return new GraphFlow({
45
46
  name: "assistant",
46
47
  schema: AgentContextSchema,
47
48
  context: {
@@ -53,17 +54,18 @@ export class Agent {
53
54
  nodes: [
54
55
  {
55
56
  name: "process",
56
- execute: async (context) => {
57
+ execute: async (context: GraphContext<typeof AgentContextSchema>) => {
57
58
  const agentContext = context as unknown as AgentContext;
58
59
  const decision = await this.executor.makeDecision(agentContext);
59
60
  context.actions = decision.actions;
60
61
  context.response = decision.response;
61
62
  },
62
- next: (context) => (context.actions.length > 0 ? ["execute"] : []),
63
+ next: (context: GraphContext<typeof AgentContextSchema>) =>
64
+ context.actions.length > 0 ? ["execute"] : [],
63
65
  },
64
66
  {
65
67
  name: "execute",
66
- execute: async (context) => {
68
+ execute: async (context: GraphContext<typeof AgentContextSchema>) => {
67
69
  console.log(`Executing actions:`);
68
70
  console.log(context.actions);
69
71
 
@@ -79,7 +81,7 @@ export class Agent {
79
81
  }
80
82
 
81
83
  public async process(input: string): Promise<AgentContext> {
82
- await this.workflow.execute("process", undefined, {
84
+ await this.workflow.execute("process", {
83
85
  input: { raw: input },
84
86
  actions: [],
85
87
  response: "",
@@ -10,6 +10,7 @@ import {
10
10
  import { BaseAgent } from "./base";
11
11
  import { AgentExecutor } from "./base/executor";
12
12
  import { LLMFactory } from "./llm-factory";
13
+ import { PromptBuilder } from "./prompt-builder";
13
14
 
14
15
  /**
15
16
  * Generic executor that handles the interaction between the agent and LLM
@@ -96,6 +97,33 @@ ${graph
96
97
  .join("\n\n");
97
98
  }
98
99
 
100
+ private async buildSystemPrompt(context: AgentContext): Promise<string> {
101
+ return new PromptBuilder()
102
+ .addSection("ROLE", this.agent.getRole())
103
+ .addSection("GOAL", this.agent.getGoal())
104
+ .addSection("BACKSTORY", this.agent.getBackstory())
105
+ .addSection(
106
+ "RECENT ACTIONS (check this before deciding what to do)",
107
+ context.knowledge || "None"
108
+ )
109
+ .addSection(
110
+ "AVAILABLE ACTIONS (only if you need)",
111
+ this.generateActionSchema()
112
+ )
113
+ .addSection(
114
+ "INSTRUCTIONS",
115
+ `
116
+ - Never take actions if an recent action matches the user's input
117
+ - If you need to take actions, structure parameters according to the action's schema
118
+ - If goal is achieved, explain that you achieved for examples:
119
+ - "I have achieved the goal"
120
+ - "I have done what I needed to do"
121
+ - "I have completed the task"
122
+ `
123
+ )
124
+ .build(context);
125
+ }
126
+
99
127
  /**
100
128
  * Makes a decision based on the current context using the LLM
101
129
  * @param {AgentContext} context - The context to base the decision on
@@ -107,50 +135,16 @@ ${graph
107
135
  chalk.dim("Analyzing context and available actions...")
108
136
  );
109
137
 
110
- const memories = await this.agent.recall(context.input.raw);
111
- if (memories.length > 0) {
112
- this.log("info", chalk.dim("Retrieved relevant memories:"));
113
- memories.forEach((m) => this.log("info", chalk.dim(`- ${m.content}`)));
114
-
115
- context.knowledge =
116
- (context.knowledge || "") +
117
- "\n" +
118
- memories.map((m) => m.content).join("\n");
119
- }
120
-
121
- const systemPrompt = `
122
- ## ROLE
123
- ${this.agent.getRole()}
124
-
125
- ## GOAL
126
- ${this.agent.getGoal()}
127
-
128
- ## BACKSTORY
129
- ${this.agent.getBackstory()}
130
-
131
- ## RECENT ACTIONS
132
- ${context.knowledge ? `${context.knowledge}\n` : "None"}
133
-
134
- ## AVAILABLE ACTIONS
135
- ${this.generateActionSchema()}
136
-
137
- ## INSTRUCTIONS
138
- - Analyze the user input and what you have done (if no action is needed, just return an empty array)
139
- - Choose appropriate actions based on their parameters
140
- - Structure parameters according to the action's schema
141
- - Look at the goal and the actions you have done, if you have achieved the goal, STOP
142
- `;
138
+ const systemPrompt = await this.buildSystemPrompt(context);
143
139
 
140
+ console.log({ systemPrompt });
144
141
  this.log("info", chalk.dim("Generating response..."));
145
142
 
146
143
  const result = await this.llm.generate(
147
144
  {
148
145
  system: systemPrompt,
149
- user: `User input: ${context.input.raw}
150
- Actions you have already done: ${
151
- context.executedActions
152
- ?.map((a) => `\n- ${a.name} => ${JSON.stringify(a.result)}`)
153
- .join("") || "None"
146
+ user: `User sent you this message at ${new Date().toISOString()}\n${
147
+ context.input.raw
154
148
  }`,
155
149
  },
156
150
  z.object({
@@ -168,7 +162,6 @@ ${graph
168
162
  response: z.string(),
169
163
  })
170
164
  );
171
-
172
165
  if (result.object.actions.length > 0) {
173
166
  this.log("success", chalk.green("Decided to take actions:"));
174
167
  result.object.actions.forEach(
@@ -239,7 +232,7 @@ ${graph
239
232
  // Execute with merged context
240
233
  const result = await workflow.execute(
241
234
  startNode,
242
- undefined,
235
+
243
236
  workflowContext
244
237
  );
245
238
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai.ntellect/core",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -2,21 +2,29 @@ import { expect } from "chai";
2
2
  import { z } from "zod";
3
3
  import { GraphController } from "../../graph/controller";
4
4
  import { GraphFlow } from "../../graph/index";
5
- import { GraphNodeConfig } from "../../types";
5
+ import { GraphContext, GraphNodeConfig } from "../../types";
6
6
 
7
7
  describe("GraphController", () => {
8
8
  const TestSchema = z.object({
9
9
  counter: z.number(),
10
10
  message: z.string(),
11
+ value: z.number().optional(),
12
+ prefix: z.string().optional(),
11
13
  });
12
14
 
13
15
  const createTestGraph = (name: string): GraphFlow<typeof TestSchema> => {
14
16
  const nodes: GraphNodeConfig<typeof TestSchema>[] = [
15
17
  {
16
18
  name: "start",
17
- execute: async (context, params) => {
18
- context.counter = params?.value ?? 0;
19
- context.message = params?.prefix ? `${params.prefix}-${name}` : name;
19
+ execute: async (context) => {
20
+ if (context.value !== undefined) {
21
+ context.counter = context.value;
22
+ }
23
+ if (context.prefix) {
24
+ context.message = `${context.prefix}-${name}`;
25
+ } else {
26
+ context.message = name;
27
+ }
20
28
  },
21
29
  },
22
30
  {
@@ -27,7 +35,7 @@ describe("GraphController", () => {
27
35
  },
28
36
  ];
29
37
 
30
- return new GraphFlow(name, {
38
+ return new GraphFlow({
31
39
  name,
32
40
  nodes,
33
41
  schema: TestSchema,
@@ -36,23 +44,21 @@ describe("GraphController", () => {
36
44
  };
37
45
 
38
46
  describe("Sequential Execution", () => {
39
- it("should execute graphs sequentially with different params and params", async () => {
47
+ it("should execute graphs sequentially with different contexts", async () => {
40
48
  const graph1 = createTestGraph("graph1");
41
49
  const graph2 = createTestGraph("graph2");
42
50
  const graph3 = createTestGraph("graph3");
43
51
 
44
- const params = [{ value: 10 }, { value: 20 }, { value: 30 }];
45
-
46
- const params2 = [
47
- { prefix: "test1" },
48
- { prefix: "test2" },
49
- { prefix: "test3" },
52
+ const contexts = [
53
+ { value: 10, prefix: "test1" },
54
+ { value: 20, prefix: "test2" },
55
+ { value: 30, prefix: "test3" },
50
56
  ];
51
57
 
52
58
  const results = await GraphController.executeSequential(
53
59
  [graph1, graph2, graph3],
54
60
  ["start", "start", "start"],
55
- params.map((value, i) => ({ ...value, prefix: params2[i].prefix }))
61
+ contexts
56
62
  );
57
63
 
58
64
  expect(results).to.have.length(3);
@@ -66,7 +72,7 @@ describe("GraphController", () => {
66
72
  expect(results[0].nodeName).to.equal("start");
67
73
  });
68
74
 
69
- it("should handle missing params and params gracefully", async () => {
75
+ it("should handle missing contexts gracefully", async () => {
70
76
  const graph1 = createTestGraph("graph1");
71
77
  const graph2 = createTestGraph("graph2");
72
78
 
@@ -89,7 +95,7 @@ describe("GraphController", () => {
89
95
  createTestGraph(`graph${i + 1}`)
90
96
  );
91
97
 
92
- const params = Array.from({ length: 5 }, (_, i) => ({
98
+ const contexts = Array.from({ length: 5 }, (_, i) => ({
93
99
  value: (i + 1) * 10,
94
100
  prefix: `test${i + 1}`,
95
101
  }));
@@ -106,7 +112,7 @@ describe("GraphController", () => {
106
112
  graphs,
107
113
  Array(5).fill("start"),
108
114
  2,
109
- params
115
+ contexts
110
116
  );
111
117
  const executionTime = Date.now() - startTime;
112
118
 
@@ -119,7 +125,7 @@ describe("GraphController", () => {
119
125
  });
120
126
 
121
127
  it("should handle errors in parallel execution", async () => {
122
- const errorGraph = new GraphFlow("errorGraph", {
128
+ const errorGraph = new GraphFlow({
123
129
  name: "errorGraph",
124
130
  nodes: [
125
131
  {
@@ -188,4 +194,43 @@ describe("GraphController", () => {
188
194
  ]);
189
195
  });
190
196
  });
197
+
198
+ it("should wait for correlated events", async function () {
199
+ const graph = new GraphFlow({
200
+ name: "test",
201
+ schema: TestSchema,
202
+ nodes: [
203
+ {
204
+ name: "correlatedEventsNode",
205
+ when: {
206
+ events: ["eventA", "eventB"],
207
+ timeout: 1000,
208
+ strategy: {
209
+ type: "correlate",
210
+ correlation: (events) => {
211
+ const eventA = events.find((e) => e.type === "eventA");
212
+ const eventB = events.find((e) => e.type === "eventB");
213
+ return eventA?.payload?.id === eventB?.payload?.id;
214
+ },
215
+ },
216
+ },
217
+ execute: async (context: GraphContext<typeof TestSchema>) => {
218
+ context.message = "Correlated events received";
219
+ },
220
+ },
221
+ ],
222
+ context: { value: 0, counter: 0, message: "" },
223
+ });
224
+
225
+ const execution = graph.execute("correlatedEventsNode");
226
+
227
+ // Émettre les événements corrélés
228
+ setTimeout(() => {
229
+ graph.emit("eventA", { id: "123", status: "completed" });
230
+ graph.emit("eventB", { id: "123", status: "available" });
231
+ }, 100);
232
+
233
+ await execution;
234
+ expect(graph.getContext().message).to.equal("Correlated events received");
235
+ });
191
236
  });