@ai.ntellect/core 0.6.13 → 0.6.14
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/README.md +175 -332
- package/graph/graph.ts +0 -2
- package/package.json +1 -1
package/README.md
CHANGED
@@ -1,404 +1,247 @@
|
|
1
1
|
# @ai.ntellect/core
|
2
2
|
|
3
|
-
|
3
|
+
A powerful framework for building AI-powered applications with graph-based workflows, memory management, and embedding services.
|
4
4
|
|
5
|
-
##
|
5
|
+
## Features
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
- **
|
10
|
-
- **
|
11
|
-
- **Controller-based orchestration** of multiple workflows
|
12
|
-
- **Subgraph** delegation for modular design
|
13
|
-
- **Memory management** for agents and chatbots (e.g., storing user context or embeddings)
|
14
|
-
- **Integration** with real-time notifiers and persistence layers
|
15
|
-
|
16
|
-
Whether you’re building a data pipeline, an AI-driven bot, or an automated blockchain process, @ai.ntellect/core offers a concise yet powerful suite of tooling to handle the complexity of stateful, event-driven orchestration.
|
17
|
-
|
18
|
-
---
|
7
|
+
- 🔄 **Graph-based workflow engine**: Build complex, type-safe workflows with retry mechanisms and error handling
|
8
|
+
- 🧠 **Memory management**: Store and retrieve AI-related data with multiple storage adapters (Meilisearch, Redis)
|
9
|
+
- 🔍 **Embedding services**: Generate and compare text embeddings for semantic search and similarity matching
|
10
|
+
- ⏰ **Task scheduling**: Schedule and manage recurring tasks with cron expressions
|
19
11
|
|
20
12
|
## Installation
|
21
13
|
|
22
|
-
### Prerequisites
|
23
|
-
|
24
|
-
- **Node.js** (14.x or higher recommended)
|
25
|
-
- A package manager such as **npm** or **yarn**
|
26
|
-
|
27
|
-
### Installing the package
|
28
|
-
|
29
14
|
```bash
|
30
15
|
npm install @ai.ntellect/core
|
31
16
|
```
|
32
17
|
|
33
|
-
|
34
|
-
|
35
|
-
```bash
|
36
|
-
yarn add @ai.ntellect/core
|
37
|
-
```
|
38
|
-
|
39
|
-
Or using pnpm:
|
40
|
-
|
41
|
-
```bash
|
42
|
-
pnpm add @ai.ntellect/core
|
43
|
-
```
|
44
|
-
|
45
|
-
### Initial configuration
|
18
|
+
## Core components
|
46
19
|
|
47
|
-
|
20
|
+
The Graph system is the heart of the framework, enabling dynamic and highly flexible workflows.
|
48
21
|
|
49
|
-
|
50
|
-
import { GraphEngine, GraphController } from "@ai.ntellect/core";
|
51
|
-
```
|
22
|
+
### Basic graph structure
|
52
23
|
|
53
|
-
|
54
|
-
|
24
|
+
```typescript
|
25
|
+
import { Graph, GraphContext } from "@ai.ntellect/core";
|
26
|
+
import { z } from "zod";
|
55
27
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
### Graph
|
63
|
-
|
64
|
-
A **Graph** is a directed structure describing a workflow. It consists of:
|
65
|
-
|
66
|
-
- **Nodes**: the tasks or steps in your workflow
|
67
|
-
- **Edges (relationships)**: transitions from one node to another
|
68
|
-
|
69
|
-
You define a **Graph** via a `GraphDefinition`, specifying:
|
70
|
-
|
71
|
-
1. A unique **name** for the graph
|
72
|
-
2. An **entryNode** (starting point)
|
73
|
-
3. A map of **node objects** (each one describes a single node’s logic and transitions)
|
74
|
-
|
75
|
-
#### Why use graphs?
|
76
|
-
|
77
|
-
- **Clear visualization**: easily see the flow of tasks, including parallel branches.
|
78
|
-
- **Condition-based transitions**: skip or filter nodes on the fly.
|
79
|
-
- **Subgraph usage**: encapsulate common flows for reuse.
|
80
|
-
|
81
|
-
#### Example of a simple graph definition
|
28
|
+
// Context Schema Definition
|
29
|
+
const contextSchema = z.object({
|
30
|
+
input: z.string(),
|
31
|
+
result: z.number().optional(),
|
32
|
+
error: z.string().optional(),
|
33
|
+
});
|
82
34
|
|
83
|
-
|
84
|
-
const
|
85
|
-
name: "
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
35
|
+
// Graph Creation
|
36
|
+
const workflow = new Graph("processWorkflow", {
|
37
|
+
name: "workflow",
|
38
|
+
nodes: [
|
39
|
+
{
|
40
|
+
name: "inputValidation",
|
41
|
+
execute: async (context) => {
|
42
|
+
if (context.input.length < 3) {
|
43
|
+
context.error = "Input too short";
|
44
|
+
}
|
92
45
|
},
|
93
|
-
|
46
|
+
next: ["processing"],
|
94
47
|
},
|
95
|
-
|
96
|
-
name: "
|
97
|
-
|
98
|
-
|
99
|
-
|
48
|
+
{
|
49
|
+
name: "processing",
|
50
|
+
condition: (context) => !context.error,
|
51
|
+
execute: async (context) => {
|
52
|
+
context.result = context.input.length;
|
100
53
|
},
|
101
|
-
|
54
|
+
next: ["finalValidation"],
|
102
55
|
},
|
103
|
-
|
104
|
-
name: "
|
105
|
-
execute: async (
|
106
|
-
|
56
|
+
{
|
57
|
+
name: "finalValidation",
|
58
|
+
execute: async (context) => {
|
59
|
+
if (context.result && context.result < 10) {
|
60
|
+
context.error = "Result too small";
|
61
|
+
}
|
62
|
+
},
|
107
63
|
},
|
64
|
+
],
|
65
|
+
initialContext: {
|
66
|
+
input: "",
|
67
|
+
result: undefined,
|
68
|
+
error: undefined,
|
69
|
+
},
|
70
|
+
validator: contextSchema,
|
71
|
+
globalErrorHandler: (error, context) => {
|
72
|
+
console.error("Global error:", error);
|
108
73
|
},
|
109
|
-
};
|
74
|
+
});
|
110
75
|
```
|
111
76
|
|
112
|
-
|
113
|
-
|
114
|
-
A **Node** is a fundamental unit of work within a graph. Each node defines:
|
115
|
-
|
116
|
-
- **name**: a unique identifier within the graph
|
117
|
-
- **execute**: the asynchronous function that implements the node’s logic
|
118
|
-
- **condition** (optional): a function returning a boolean determining if this node should run
|
119
|
-
- **relationships**: an array of transitions to subsequent nodes
|
120
|
-
- **events** (optional): an array of event names that can trigger the node (bypassing usual transitions)
|
77
|
+
## Advanced graph features
|
121
78
|
|
122
|
-
|
79
|
+
### 1. Nodes with conditions
|
123
80
|
|
124
|
-
|
81
|
+
Add sophisticated conditions for node execution:
|
125
82
|
|
126
|
-
```
|
83
|
+
```typescript
|
127
84
|
{
|
128
|
-
name: "
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
}
|
85
|
+
name: "conditionalNode",
|
86
|
+
// Execute the node only if the condition is met
|
87
|
+
condition: (context) => context.result > 10,
|
88
|
+
execute: async (context) => {
|
89
|
+
// Conditional logic
|
90
|
+
}
|
134
91
|
}
|
135
92
|
```
|
136
93
|
|
137
|
-
|
138
|
-
|
139
|
-
### GraphEngine
|
140
|
-
|
141
|
-
#### Overview
|
142
|
-
|
143
|
-
The **GraphEngine** (often shortened to “engine”) is responsible for:
|
144
|
-
|
145
|
-
- Loading a `GraphDefinition`
|
146
|
-
- Executing its nodes according to **relationships** and optional **conditions**
|
147
|
-
- Handling **state** updates after each node execution
|
148
|
-
- Managing **event** emissions and listening for event-driven nodes
|
149
|
-
- Allowing **parallel** or **sequential** node execution
|
150
|
-
- Managing **subgraphs** if your workflow references external graph definitions
|
151
|
-
|
152
|
-
```ts
|
153
|
-
import { GraphEngine } from "ai.ntellect/core";
|
154
|
-
|
155
|
-
const engine = new GraphEngine(myGraphDefinition);
|
156
|
-
await engine.execute({ context: { user: "Alice" } }, "start");
|
157
|
-
```
|
158
|
-
|
159
|
-
### GraphController
|
160
|
-
|
161
|
-
#### Overview
|
162
|
-
|
163
|
-
The **GraphController** provides a **high-level orchestration** mechanism for multiple graphs. Instead of running a single workflow, you can define **actions**—each tied to a particular workflow—and the controller executes them in sequence (or other patterns) based on your configuration.
|
164
|
-
|
165
|
-
```ts
|
166
|
-
import { GraphController } from "ai.ntellect/core";
|
94
|
+
### 2. Error handling
|
167
95
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
console.
|
183
|
-
// => final state after running 'my-simple-graph'
|
184
|
-
```
|
185
|
-
|
186
|
-
**Use cases**:
|
187
|
-
|
188
|
-
- **Batch execution** of multiple workflows
|
189
|
-
- **Multi-tenant** orchestration where each tenant’s configuration is an “action”
|
190
|
-
- **Chained flows**: run workflow A, then run workflow B with the result of A
|
191
|
-
|
192
|
-
### Memory management
|
193
|
-
|
194
|
-
In advanced workflows, especially with chatbots or AI agents, you might want to maintain a **memory** of previous interactions or references. @ai.ntellect/core accommodates this via an abstract class **BaseMemory** and a dedicated **BaseMemoryService** for storing and retrieving data. This can be used to store embeddings, historical context, or any ephemeral data needed for your workflows.
|
195
|
-
|
196
|
-
```ts
|
197
|
-
import { BaseMemory } from "ai.ntellect/core";
|
198
|
-
|
199
|
-
// Example concrete class
|
200
|
-
class MyMemory extends BaseMemory {
|
201
|
-
async init(): Promise<void> {
|
202
|
-
/*...*/
|
203
|
-
}
|
204
|
-
async createMemory(input): Promise<BaseMemoryType | undefined> {
|
205
|
-
/*...*/
|
206
|
-
}
|
207
|
-
async getMemoryById(id, roomId): Promise<BaseMemoryType | null> {
|
208
|
-
/*...*/
|
96
|
+
```typescript
|
97
|
+
{
|
98
|
+
name: "unreliableOperation",
|
99
|
+
retry: {
|
100
|
+
// Maximum number of attempts
|
101
|
+
maxAttempts: 3,
|
102
|
+
// Delay between attempts
|
103
|
+
delay: 1000 // 1 second
|
104
|
+
},
|
105
|
+
execute: async (context) => {
|
106
|
+
// Potentially unstable operation
|
107
|
+
},
|
108
|
+
// Node-specific error handler
|
109
|
+
onError: (error) => {
|
110
|
+
console.warn("Node error:", error);
|
209
111
|
}
|
210
|
-
// ... other methods
|
211
112
|
}
|
212
113
|
```
|
213
114
|
|
214
|
-
|
215
|
-
|
216
|
-
- In-memory
|
217
|
-
- Redis / Key-value stores
|
218
|
-
- SQL / NoSQL databases
|
219
|
-
|
220
|
-
**Key benefits**:
|
221
|
-
|
222
|
-
- Store query embeddings for AI-based search
|
223
|
-
- Maintain user session context (e.g., conversation flows)
|
224
|
-
- Rapidly retrieve and update relevant data at runtime
|
225
|
-
|
226
|
-
---
|
227
|
-
|
228
|
-
## Advanced usage and features
|
229
|
-
|
230
|
-
### Subgraphs for modularity
|
231
|
-
|
232
|
-
Nodes can delegate execution to a **subgraph**, enabling large workflows to be broken into reusable components:
|
233
|
-
|
234
|
-
```ts
|
235
|
-
const subGraphEngine = new GraphEngine(subGraphDef);
|
236
|
-
mainGraphEngine.addSubGraph(subGraphEngine, "sub-start", "sub-workflow");
|
237
|
-
```
|
238
|
-
|
239
|
-
**Why subgraphs**:
|
240
|
-
|
241
|
-
- **Reusability**: common routines can be maintained independently
|
242
|
-
- **Maintainability**: isolate large logic in smaller workflows
|
243
|
-
|
244
|
-
### Parallel execution
|
115
|
+
### 3. Dynamic and parallel execution
|
245
116
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
[
|
252
|
-
|
117
|
+
```typescript
|
118
|
+
// Execute multiple graphs in parallel
|
119
|
+
const results = await GraphController.executeParallel(
|
120
|
+
[graph1, graph2, graph3],
|
121
|
+
["startNode1", "startNode2", "startNode3"],
|
122
|
+
[context1, context2, context3],
|
123
|
+
undefined, // parameters
|
124
|
+
3 // concurrency limit
|
253
125
|
);
|
254
126
|
```
|
255
127
|
|
256
|
-
###
|
128
|
+
### 4. Events
|
257
129
|
|
258
|
-
|
130
|
+
```typescript
|
131
|
+
workflow.on("nodeCompleted", (data) => {
|
132
|
+
console.log(`Node ${data.nodeName} completed`);
|
133
|
+
});
|
259
134
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
},
|
265
|
-
};
|
266
|
-
engine.setNotifier(notifier);
|
135
|
+
// Emit custom events
|
136
|
+
await workflow.emit("customEvent", {
|
137
|
+
additionalData: "value",
|
138
|
+
});
|
267
139
|
```
|
268
140
|
|
269
|
-
###
|
270
|
-
|
271
|
-
For long-running or mission-critical workflows, implement a **Persistence** interface:
|
141
|
+
### 5. Dynamic graph modification
|
272
142
|
|
273
|
-
```
|
274
|
-
|
275
|
-
|
276
|
-
|
143
|
+
```typescript
|
144
|
+
// Dynamically add nodes
|
145
|
+
workflow.addNode({
|
146
|
+
name: "newNode",
|
147
|
+
execute: async (context) => {
|
148
|
+
// New logic
|
277
149
|
},
|
278
|
-
|
279
|
-
/* retrieve from DB */ return null;
|
280
|
-
},
|
281
|
-
};
|
282
|
-
engine.setPersistence(myPersistence);
|
283
|
-
```
|
150
|
+
});
|
284
151
|
|
285
|
-
|
152
|
+
// Remove nodes
|
153
|
+
workflow.removeNode("obsoleteNode");
|
154
|
+
```
|
286
155
|
|
287
|
-
|
156
|
+
### 6. Context validation with Zod
|
288
157
|
|
289
|
-
|
158
|
+
Use Zod for runtime context validation:
|
290
159
|
|
291
|
-
|
160
|
+
```typescript
|
161
|
+
const strictContextSchema = z.object({
|
162
|
+
// Define precise rules
|
163
|
+
input: z.string().min(3).max(100),
|
164
|
+
result: z.number().positive(),
|
165
|
+
timestamp: z.date(),
|
166
|
+
});
|
292
167
|
|
293
|
-
|
294
|
-
|
295
|
-
|
168
|
+
const workflow = new Graph("strictWorkflow", {
|
169
|
+
// The validator will check each context modification
|
170
|
+
validator: strictContextSchema,
|
171
|
+
});
|
172
|
+
```
|
296
173
|
|
297
|
-
|
298
|
-
import { GraphDefinition } from "@/types";
|
299
|
-
// Assume Agent, MyContext, isNotStopped, shouldRetry, etc. are defined
|
174
|
+
## Complete example: Data processing workflow
|
300
175
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
nodes: {
|
309
|
-
orchestrator: {
|
310
|
-
name: "orchestrator",
|
311
|
-
description: "Make a decision following the current context",
|
312
|
-
execute: async () => {
|
313
|
-
/* your orchestrator logic */
|
314
|
-
},
|
315
|
-
condition: (state) => isNotStopped(state) || shouldRetry(state),
|
316
|
-
relationships: [
|
317
|
-
{ name: "controller", description: "Execute multiple workflows" },
|
318
|
-
{ name: "agenda", description: "Schedule actions for the future" },
|
319
|
-
{
|
320
|
-
name: "interpreter",
|
321
|
-
description: "Interpret the results of actions",
|
322
|
-
},
|
323
|
-
],
|
324
|
-
},
|
325
|
-
controller: {
|
326
|
-
name: "controller",
|
327
|
-
description: "Execute multiple workflows if available",
|
328
|
-
execute: async () => {
|
329
|
-
/* handle or queue workflow actions */
|
330
|
-
},
|
331
|
-
condition: () => {
|
332
|
-
const currentState = agent.graph.getState();
|
333
|
-
return hasActions(currentState) && isNotStopped(currentState);
|
176
|
+
```typescript
|
177
|
+
const dataProcessingWorkflow = new Graph("dataProcessor", {
|
178
|
+
nodes: [
|
179
|
+
{
|
180
|
+
name: "dataFetch",
|
181
|
+
execute: async (context) => {
|
182
|
+
context.rawData = await fetchData();
|
334
183
|
},
|
335
|
-
|
184
|
+
next: ["dataValidation"],
|
336
185
|
},
|
337
|
-
|
338
|
-
name: "
|
339
|
-
|
340
|
-
execute: async () => {
|
341
|
-
|
186
|
+
{
|
187
|
+
name: "dataValidation",
|
188
|
+
condition: (context) => context.rawData.length > 0,
|
189
|
+
execute: async (context) => {
|
190
|
+
context.validatedData = validateData(context.rawData);
|
342
191
|
},
|
343
|
-
|
192
|
+
next: ["dataTransformation"],
|
344
193
|
},
|
345
|
-
|
346
|
-
name: "
|
347
|
-
|
348
|
-
|
349
|
-
/* interpret results, maybe using memory */
|
350
|
-
},
|
351
|
-
condition: () => {
|
352
|
-
const currentState = agent.graph.getState();
|
353
|
-
return (
|
354
|
-
isInterpreterDefined(currentState) &&
|
355
|
-
isResultsDefined(currentState) &&
|
356
|
-
isStopped(currentState)
|
357
|
-
);
|
194
|
+
{
|
195
|
+
name: "dataTransformation",
|
196
|
+
execute: async (context) => {
|
197
|
+
context.processedData = transformData(context.validatedData);
|
358
198
|
},
|
359
|
-
|
199
|
+
next: ["dataStorage"],
|
360
200
|
},
|
361
|
-
|
362
|
-
name: "
|
363
|
-
|
364
|
-
|
365
|
-
/* store or retrieve conversation states */
|
366
|
-
},
|
367
|
-
condition: () => {
|
368
|
-
const currentState = agent.graph.getState();
|
369
|
-
return isResultsDefined(currentState);
|
201
|
+
{
|
202
|
+
name: "dataStorage",
|
203
|
+
execute: async (context) => {
|
204
|
+
await storeData(context.processedData);
|
370
205
|
},
|
371
206
|
},
|
372
|
-
|
207
|
+
],
|
373
208
|
});
|
374
209
|
```
|
375
210
|
|
376
|
-
|
211
|
+
## Key points
|
377
212
|
|
378
|
-
|
213
|
+
- **Total flexibility**: Create complex workflows with great freedom
|
214
|
+
- **Type safety**: Runtime context and parameter validation
|
215
|
+
- **Dynamic management**: Modify graphs during execution
|
216
|
+
- **Resilience**: Integrated retry and error handling mechanisms
|
379
217
|
|
380
|
-
##
|
218
|
+
## GraphController: Advanced execution strategies
|
381
219
|
|
382
|
-
|
383
|
-
2. **Data pipeline**: Stream logs into a transformation flow with parallel processing and conditional branches.
|
384
|
-
3. **AI bots**: Manage conversation state, memory, and advanced decision trees for chat-based agents.
|
385
|
-
4. **Blockchain**: Sequence complex contract interactions, handle parallel on-chain calls, and revert safely on errors.
|
386
|
-
5. **Task scheduling**: Combine GraphController with multiple workflows to handle enterprise-wide daily or weekly tasks.
|
220
|
+
### Sequential execution
|
387
221
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
222
|
+
```typescript
|
223
|
+
const sequentialResults = await GraphController.executeSequential(
|
224
|
+
[graph1, graph2],
|
225
|
+
["startNode1", "startNode2"],
|
226
|
+
[context1, context2]
|
227
|
+
);
|
228
|
+
```
|
393
229
|
|
394
|
-
###
|
230
|
+
### Parallel execution with concurrency control
|
395
231
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
232
|
+
```typescript
|
233
|
+
const parallelResults = await GraphController.executeParallel(
|
234
|
+
multipleGraphs,
|
235
|
+
startNodes,
|
236
|
+
inputContexts,
|
237
|
+
inputParams,
|
238
|
+
3 // Maximum 3 graphs executing simultaneously
|
239
|
+
);
|
240
|
+
```
|
401
241
|
|
402
|
-
|
242
|
+
## Performance considerations
|
403
243
|
|
404
|
-
Use
|
244
|
+
- Use `executeParallel` for independent workflows
|
245
|
+
- Implement appropriate concurrency limits
|
246
|
+
- Monitor context size and complexity
|
247
|
+
- Leverage Zod for efficient runtime validation
|
package/graph/graph.ts
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
import { GraphConfig, GraphContext, GraphDefinition, Node } from "@/types";
|
2
|
-
import { configDotenv } from "dotenv";
|
3
2
|
import EventEmitter from "events";
|
4
3
|
import { ZodSchema } from "zod";
|
5
|
-
configDotenv();
|
6
4
|
|
7
5
|
// Classe Graph avec un contexte typé
|
8
6
|
export class Graph<T extends ZodSchema> {
|