@ai.ntellect/core 0.7.1 → 0.7.4
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 +13 -28
- package/dist/graph/controller.d.ts +33 -0
- package/dist/graph/controller.d.ts.map +1 -0
- package/dist/graph/controller.js +73 -0
- package/dist/graph/controller.js.map +1 -0
- package/dist/graph/event-manager.d.ts +92 -0
- package/dist/graph/event-manager.d.ts.map +1 -0
- package/dist/graph/event-manager.js +244 -0
- package/dist/graph/event-manager.js.map +1 -0
- package/dist/graph/index.d.ts +157 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +299 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/logger.d.ts +46 -0
- package/dist/graph/logger.d.ts.map +1 -0
- package/dist/graph/logger.js +69 -0
- package/dist/graph/logger.js.map +1 -0
- package/dist/graph/node.d.ts +103 -0
- package/dist/graph/node.d.ts.map +1 -0
- package/dist/graph/node.js +284 -0
- package/dist/graph/node.js.map +1 -0
- package/dist/graph/observer.d.ts +113 -0
- package/dist/graph/observer.d.ts.map +1 -0
- package/dist/graph/observer.js +199 -0
- package/dist/graph/observer.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/index.d.ts +447 -0
- package/dist/interfaces/index.d.ts.map +1 -0
- package/dist/interfaces/index.js +75 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/modules/agenda/adapters/node-cron/index.d.ts +17 -0
- package/dist/modules/agenda/adapters/node-cron/index.d.ts.map +1 -0
- package/dist/modules/agenda/adapters/node-cron/index.js +30 -0
- package/dist/modules/agenda/adapters/node-cron/index.js.map +1 -0
- package/dist/modules/agenda/index.d.ts +63 -0
- package/dist/modules/agenda/index.d.ts.map +1 -0
- package/dist/modules/agenda/index.js +141 -0
- package/dist/modules/agenda/index.js.map +1 -0
- package/dist/modules/embedding/adapters/ai/index.d.ts +29 -0
- package/dist/modules/embedding/adapters/ai/index.d.ts.map +1 -0
- package/dist/modules/embedding/adapters/ai/index.js +58 -0
- package/dist/modules/embedding/adapters/ai/index.js.map +1 -0
- package/dist/modules/embedding/index.d.ts +36 -0
- package/dist/modules/embedding/index.d.ts.map +1 -0
- package/dist/modules/embedding/index.js +60 -0
- package/dist/modules/embedding/index.js.map +1 -0
- package/dist/modules/memory/adapters/in-memory/index.d.ts +120 -0
- package/dist/modules/memory/adapters/in-memory/index.d.ts.map +1 -0
- package/dist/modules/memory/adapters/in-memory/index.js +211 -0
- package/dist/modules/memory/adapters/in-memory/index.js.map +1 -0
- package/dist/modules/memory/adapters/meilisearch/index.d.ts +110 -0
- package/dist/modules/memory/adapters/meilisearch/index.d.ts.map +1 -0
- package/dist/modules/memory/adapters/meilisearch/index.js +321 -0
- package/dist/modules/memory/adapters/meilisearch/index.js.map +1 -0
- package/dist/modules/memory/adapters/redis/index.d.ts +82 -0
- package/dist/modules/memory/adapters/redis/index.d.ts.map +1 -0
- package/dist/modules/memory/adapters/redis/index.js +159 -0
- package/dist/modules/memory/adapters/redis/index.js.map +1 -0
- package/dist/modules/memory/index.d.ts +67 -0
- package/dist/modules/memory/index.d.ts.map +1 -0
- package/dist/modules/memory/index.js +104 -0
- package/dist/modules/memory/index.js.map +1 -0
- package/dist/types/index.d.ts +166 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/generate-action-schema.d.ts +5 -0
- package/dist/utils/generate-action-schema.d.ts.map +1 -0
- package/dist/utils/generate-action-schema.js +44 -0
- package/dist/utils/generate-action-schema.js.map +1 -0
- package/dist/utils/header-builder.d.ts +12 -0
- package/dist/utils/header-builder.d.ts.map +1 -0
- package/dist/utils/header-builder.js +35 -0
- package/dist/utils/header-builder.js.map +1 -0
- package/graph/event-manager.ts +9 -2
- package/graph/node.ts +25 -63
- package/graph/observer.ts +17 -9
- package/package.json +5 -1
- package/test/graph/controller.test.ts +0 -0
- package/test/graph/event-manager.test.ts +72 -0
- package/test/graph/index.test.ts +41 -34
- package/test/graph/node.test.ts +197 -0
- package/tsconfig.json +13 -2
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"generate-action-schema.js","sourceRoot":"","sources":["../../utils/generate-action-schema.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AAGjB,MAAM,oBAAoB,GAAG,CAAC,MAAwB,EAAE,EAAE;IAC/D,OAAO,MAAM;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM;YAC/B,CAAC,CAAC,IAAA,uBAAe,EAAC,QAAQ,CAAC,MAAM,CAAC;YAClC,CAAC,CAAC,eAAe,CAAC;QACpB,OAAO,aAAa,KAAK,CAAC,IAAI,iBAAiB,SAAS,EAAE,CAAC;IAC7D,CAAC,CAAC;SACD,IAAI,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC,CAAC;AAVW,QAAA,oBAAoB,wBAU/B;AAEK,MAAM,eAAe,GAAG,CAAC,MAAiB,EAAU,EAAE;IAC3D,IAAI,MAAM,YAAY,OAAC,CAAC,SAAS,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YAC1C,MAAM,WAAW,GAAI,KAAa,CAAC,IAAI,CAAC,WAAW,CAAC;YACpD,MAAM,SAAS,GAAG,IAAA,uBAAe,EAAC,KAAkB,CAAC,CAAC;YACtD,OAAO,WAAW;gBAChB,CAAC,CAAC,GAAG,GAAG,KAAK,SAAS,OAAO,WAAW,EAAE;gBAC1C,CAAC,CAAC,GAAG,GAAG,KAAK,SAAS,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,OAAO,aAAa,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IAC5C,CAAC;IAED,IAAI,MAAM,YAAY,OAAC,CAAC,QAAQ,EAAE,CAAC;QACjC,OAAO,WAAW,IAAA,uBAAe,EAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;IACvD,CAAC;IAED,IAAI,MAAM,YAAY,OAAC,CAAC,SAAS,EAAE,CAAC;QAClC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,IAAI,MAAM,YAAY,OAAC,CAAC,SAAS,EAAE,CAAC;QAClC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,IAAI,MAAM,YAAY,OAAC,CAAC,UAAU,EAAE,CAAC;QACnC,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,OAAO,aAAa,CAAC;AACvB,CAAC,CAAC;AA9BW,QAAA,eAAe,mBA8B1B"}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
type HeaderValue = string | string[] | undefined;
|
2
|
+
export declare class LLMHeaderBuilder {
|
3
|
+
private headers;
|
4
|
+
private _result;
|
5
|
+
constructor();
|
6
|
+
addHeader(key: string, value: HeaderValue): LLMHeaderBuilder;
|
7
|
+
valueOf(): string;
|
8
|
+
toString(): string;
|
9
|
+
static create(): LLMHeaderBuilder;
|
10
|
+
}
|
11
|
+
export {};
|
12
|
+
//# sourceMappingURL=header-builder.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"header-builder.d.ts","sourceRoot":"","sources":["../../utils/header-builder.ts"],"names":[],"mappings":"AAAA,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;AAEjD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,OAAO,CAAS;;IAOxB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,gBAAgB;IAiB5D,OAAO,IAAI,MAAM;IAIjB,QAAQ,IAAI,MAAM;IAIlB,MAAM,CAAC,MAAM,IAAI,gBAAgB;CAGlC"}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.LLMHeaderBuilder = void 0;
|
4
|
+
class LLMHeaderBuilder {
|
5
|
+
constructor() {
|
6
|
+
this.headers = new Map();
|
7
|
+
this._result = "";
|
8
|
+
}
|
9
|
+
addHeader(key, value) {
|
10
|
+
if (Array.isArray(value)) {
|
11
|
+
this.headers.set(key, value.join("\n"));
|
12
|
+
}
|
13
|
+
else {
|
14
|
+
this.headers.set(key, value);
|
15
|
+
}
|
16
|
+
// Build result immediately
|
17
|
+
this._result = Array.from(this.headers.entries())
|
18
|
+
.filter(([_, value]) => value !== undefined)
|
19
|
+
.map(([key, value]) => `# ${key}: ${value}`)
|
20
|
+
.join("\n")
|
21
|
+
.trim();
|
22
|
+
return this;
|
23
|
+
}
|
24
|
+
valueOf() {
|
25
|
+
return this._result;
|
26
|
+
}
|
27
|
+
toString() {
|
28
|
+
return this._result;
|
29
|
+
}
|
30
|
+
static create() {
|
31
|
+
return new LLMHeaderBuilder();
|
32
|
+
}
|
33
|
+
}
|
34
|
+
exports.LLMHeaderBuilder = LLMHeaderBuilder;
|
35
|
+
//# sourceMappingURL=header-builder.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"header-builder.js","sourceRoot":"","sources":["../../utils/header-builder.ts"],"names":[],"mappings":";;;AAEA,MAAa,gBAAgB;IAI3B;QACE,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;IACpB,CAAC;IAED,SAAS,CAAC,GAAW,EAAE,KAAkB;QACvC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;QAED,2BAA2B;QAC3B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;aAC9C,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC;aAC3C,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,KAAK,EAAE,CAAC;aAC3C,IAAI,CAAC,IAAI,CAAC;aACV,IAAI,EAAE,CAAC;QAEV,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,MAAM,CAAC,MAAM;QACX,OAAO,IAAI,gBAAgB,EAAE,CAAC;IAChC,CAAC;CACF;AArCD,4CAqCC"}
|
package/graph/event-manager.ts
CHANGED
@@ -72,9 +72,16 @@ export class GraphEventManager<T extends ZodSchema> {
|
|
72
72
|
payload?: P,
|
73
73
|
context?: GraphContext<T>
|
74
74
|
): void {
|
75
|
-
|
75
|
+
// Éviter la double imbrication des événements
|
76
|
+
const event = {
|
77
|
+
type,
|
78
|
+
payload,
|
79
|
+
timestamp: Date.now(),
|
80
|
+
};
|
81
|
+
|
82
|
+
// Émettre l'événement une seule fois
|
76
83
|
this.eventSubject.next(event);
|
77
|
-
this.eventEmitter.emit(type,
|
84
|
+
this.eventEmitter.emit(type, payload);
|
78
85
|
}
|
79
86
|
|
80
87
|
/**
|
package/graph/node.ts
CHANGED
@@ -72,52 +72,41 @@ export class GraphNode<T extends ZodSchema> {
|
|
72
72
|
const node = this.nodes.get(nodeName);
|
73
73
|
if (!node) throw new Error(`Node "${nodeName}" not found.`);
|
74
74
|
|
75
|
-
this.logger.addLog(`🚀 Starting node "${nodeName}
|
76
|
-
this.emitEvent("nodeStarted", { name: nodeName, context
|
75
|
+
this.logger.addLog(`🚀 Starting node "${nodeName}`);
|
76
|
+
this.emitEvent("nodeStarted", { name: nodeName, context });
|
77
77
|
|
78
78
|
try {
|
79
|
+
// Vérifier la condition avant d'exécuter
|
80
|
+
if (node.condition && !node.condition(context)) {
|
81
|
+
this.logger.addLog(
|
82
|
+
`⏭️ Skipping node "${nodeName}" - condition not met`
|
83
|
+
);
|
84
|
+
return;
|
85
|
+
}
|
86
|
+
|
79
87
|
const contextProxy = new Proxy(context, {
|
80
88
|
set: (target, prop, value) => {
|
81
89
|
const oldValue = target[prop];
|
82
|
-
|
90
|
+
if (oldValue === value) return true;
|
83
91
|
|
92
|
+
target[prop] = value;
|
84
93
|
this.emitEvent("nodeStateChanged", {
|
85
94
|
nodeName,
|
86
|
-
name: nodeName,
|
87
95
|
property: prop.toString(),
|
88
96
|
oldValue,
|
89
97
|
newValue: value,
|
90
|
-
context:
|
98
|
+
context: target,
|
91
99
|
});
|
92
100
|
|
93
101
|
return true;
|
94
102
|
},
|
95
|
-
get: (target, prop) =>
|
96
|
-
return target[prop];
|
97
|
-
},
|
103
|
+
get: (target, prop) => target[prop],
|
98
104
|
});
|
99
105
|
|
100
|
-
|
101
|
-
|
102
|
-
`⏭️ Skipping node "${nodeName}" - condition not met`
|
103
|
-
);
|
104
|
-
return;
|
105
|
-
}
|
106
|
-
|
107
|
-
if (node.inputs) {
|
108
|
-
await this.validateInputs(node, inputs, nodeName);
|
109
|
-
}
|
110
|
-
|
111
|
-
if (node.retry && node.retry.maxAttempts > 0) {
|
112
|
-
await this.executeWithRetry(node, contextProxy, inputs, nodeName);
|
113
|
-
} else {
|
114
|
-
await node.execute(contextProxy, inputs);
|
115
|
-
}
|
116
|
-
|
117
|
-
if (node.outputs) {
|
118
|
-
await this.validateOutputs(node, contextProxy, nodeName);
|
119
|
-
}
|
106
|
+
// Exécuter le nœud
|
107
|
+
await node.execute(contextProxy, inputs);
|
120
108
|
|
109
|
+
// Gérer la suite uniquement si pas déclenché par un événement
|
121
110
|
if (!triggeredByEvent) {
|
122
111
|
const nextNodes =
|
123
112
|
typeof node.next === "function"
|
@@ -125,47 +114,20 @@ export class GraphNode<T extends ZodSchema> {
|
|
125
114
|
: node.next || [];
|
126
115
|
|
127
116
|
for (const nextNodeName of nextNodes) {
|
128
|
-
|
129
|
-
if (
|
130
|
-
nextNode &&
|
131
|
-
(!nextNode.condition || nextNode.condition(contextProxy))
|
132
|
-
) {
|
133
|
-
await this.executeNode(
|
134
|
-
nextNodeName,
|
135
|
-
contextProxy,
|
136
|
-
undefined,
|
137
|
-
false
|
138
|
-
);
|
139
|
-
}
|
117
|
+
await this.executeNode(nextNodeName, context, undefined, false);
|
140
118
|
}
|
141
119
|
}
|
142
120
|
|
143
|
-
if (!triggeredByEvent) {
|
144
|
-
await this.handleEvents(node, nodeName, contextProxy);
|
145
|
-
}
|
146
|
-
|
147
121
|
this.logger.addLog(`✅ Node "${nodeName}" executed successfully`);
|
148
|
-
this.emitEvent("nodeCompleted", {
|
149
|
-
|
150
|
-
context: { ...contextProxy },
|
151
|
-
});
|
152
|
-
} catch (error: any) {
|
153
|
-
const errorToThrow =
|
154
|
-
error instanceof Error
|
155
|
-
? error
|
156
|
-
: new Error(error.message || "Unknown error");
|
157
|
-
|
122
|
+
this.emitEvent("nodeCompleted", { name: nodeName, context });
|
123
|
+
} catch (error) {
|
158
124
|
this.logger.addLog(
|
159
|
-
`❌ Error in node "${nodeName}": ${
|
125
|
+
`❌ Error in node "${nodeName}": ${
|
126
|
+
error instanceof Error ? error.message : String(error)
|
127
|
+
}`
|
160
128
|
);
|
161
|
-
|
162
|
-
|
163
|
-
name: nodeName,
|
164
|
-
error: errorToThrow,
|
165
|
-
context,
|
166
|
-
});
|
167
|
-
|
168
|
-
throw errorToThrow;
|
129
|
+
this.emitEvent("nodeError", { name: nodeName, error, context });
|
130
|
+
throw error;
|
169
131
|
}
|
170
132
|
}
|
171
133
|
|
package/graph/observer.ts
CHANGED
@@ -6,6 +6,7 @@ import {
|
|
6
6
|
firstValueFrom,
|
7
7
|
} from "rxjs";
|
8
8
|
import {
|
9
|
+
debounceTime,
|
9
10
|
distinctUntilChanged,
|
10
11
|
filter,
|
11
12
|
map,
|
@@ -55,16 +56,23 @@ export class GraphObserver<T extends ZodSchema> {
|
|
55
56
|
} = {}
|
56
57
|
): GraphObservable<T> {
|
57
58
|
const baseObservable = new Observable<any>((subscriber) => {
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
event.type === "nodeStateChanged" ||
|
63
|
-
event.type === "nodeStarted" ||
|
64
|
-
event.type === "nodeCompleted"
|
65
|
-
),
|
59
|
+
// Combine les événements avec l'état actuel
|
60
|
+
const subscription = combineLatest([
|
61
|
+
this.eventSubject.pipe(
|
62
|
+
filter((event) => event.type === "nodeStateChanged"),
|
66
63
|
map((event) => event.payload.context),
|
67
|
-
|
64
|
+
distinctUntilChanged(
|
65
|
+
(prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
|
66
|
+
),
|
67
|
+
debounceTime(options.debounce || 100)
|
68
|
+
),
|
69
|
+
this.stateSubject,
|
70
|
+
])
|
71
|
+
.pipe(
|
72
|
+
map(([eventContext, stateContext]) => ({
|
73
|
+
...stateContext,
|
74
|
+
...eventContext,
|
75
|
+
})),
|
68
76
|
distinctUntilChanged(
|
69
77
|
(prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)
|
70
78
|
)
|
package/package.json
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ai.ntellect/core",
|
3
|
-
"version": "0.7.
|
3
|
+
"version": "0.7.4",
|
4
4
|
"description": "",
|
5
5
|
"main": "dist/index.js",
|
6
6
|
"scripts": {
|
7
7
|
"build": "rm -rf dist && tsc",
|
8
|
+
"prepare": "npm run build",
|
9
|
+
"prepublishOnly": "npm run build",
|
8
10
|
"test": "mocha -r ./node_modules/ts-node/register",
|
9
11
|
"test:all": "mocha -r ./node_modules/ts-node/register 'test/**/*.test.ts'",
|
10
12
|
"test:coverage": "nyc --reporter=text --reporter=html pnpm test",
|
@@ -24,10 +26,12 @@
|
|
24
26
|
},
|
25
27
|
"devDependencies": {
|
26
28
|
"@types/chai": "^4.3.20",
|
29
|
+
"@types/chai-as-promised": "^8.0.1",
|
27
30
|
"@types/mocha": "^10.0.10",
|
28
31
|
"@types/node": "^20",
|
29
32
|
"@types/sinon": "^17.0.3",
|
30
33
|
"chai": "^4.5.0",
|
34
|
+
"chai-as-promised": "^8.0.1",
|
31
35
|
"mocha": "^10.0.0",
|
32
36
|
"nyc": "^17.1.0",
|
33
37
|
"redis": "^4.6.13",
|
File without changes
|
@@ -0,0 +1,72 @@
|
|
1
|
+
import { expect } from "chai";
|
2
|
+
import { EventEmitter } from "events";
|
3
|
+
import { z } from "zod";
|
4
|
+
import { GraphEventManager } from "../../graph/event-manager";
|
5
|
+
|
6
|
+
describe("GraphEventManager", () => {
|
7
|
+
const TestSchema = z.object({
|
8
|
+
counter: z.number(),
|
9
|
+
message: z.string(),
|
10
|
+
});
|
11
|
+
|
12
|
+
let eventManager: GraphEventManager<typeof TestSchema>;
|
13
|
+
let eventEmitter: EventEmitter;
|
14
|
+
let events: any[] = [];
|
15
|
+
|
16
|
+
beforeEach(() => {
|
17
|
+
events = [];
|
18
|
+
eventEmitter = new EventEmitter();
|
19
|
+
eventManager = new GraphEventManager(eventEmitter, new Map(), "test", {
|
20
|
+
counter: 0,
|
21
|
+
message: "Hello",
|
22
|
+
});
|
23
|
+
});
|
24
|
+
|
25
|
+
it("should emit events without duplication", () => {
|
26
|
+
const emittedEvents: any[] = [];
|
27
|
+
eventEmitter.on("test", (event) => emittedEvents.push(event));
|
28
|
+
|
29
|
+
eventManager.emitEvent("test", { data: "test" });
|
30
|
+
|
31
|
+
expect(emittedEvents).to.have.lengthOf(1);
|
32
|
+
expect(emittedEvents[0]).to.deep.equal({ data: "test" });
|
33
|
+
});
|
34
|
+
|
35
|
+
it("should handle nodeStateChanged events correctly", () => {
|
36
|
+
const stateChanges: any[] = [];
|
37
|
+
eventEmitter.on("nodeStateChanged", (event) => stateChanges.push(event));
|
38
|
+
|
39
|
+
eventManager.emitEvent("nodeStateChanged", {
|
40
|
+
nodeName: "test",
|
41
|
+
property: "counter",
|
42
|
+
oldValue: 0,
|
43
|
+
newValue: 1,
|
44
|
+
context: { counter: 1, message: "Hello" },
|
45
|
+
});
|
46
|
+
|
47
|
+
expect(stateChanges).to.have.lengthOf(1);
|
48
|
+
expect(stateChanges[0].nodeName).to.equal("test");
|
49
|
+
expect(stateChanges[0].context.counter).to.equal(1);
|
50
|
+
});
|
51
|
+
|
52
|
+
it("should setup and cleanup event listeners", () => {
|
53
|
+
const nodes = new Map();
|
54
|
+
nodes.set("test", {
|
55
|
+
name: "test",
|
56
|
+
events: ["customEvent"],
|
57
|
+
execute: async () => {},
|
58
|
+
});
|
59
|
+
|
60
|
+
eventManager = new GraphEventManager(eventEmitter, nodes, "test", {
|
61
|
+
counter: 0,
|
62
|
+
message: "Hello",
|
63
|
+
});
|
64
|
+
|
65
|
+
eventManager.setupEventListeners();
|
66
|
+
expect(eventEmitter.listenerCount("customEvent")).to.equal(1);
|
67
|
+
|
68
|
+
// Réinitialiser les listeners
|
69
|
+
eventManager.setupEventListeners();
|
70
|
+
expect(eventEmitter.listenerCount("customEvent")).to.equal(1);
|
71
|
+
});
|
72
|
+
});
|
package/test/graph/index.test.ts
CHANGED
@@ -37,7 +37,7 @@ type TestSchema = typeof TestSchema;
|
|
37
37
|
* The tests use a simple numeric value-based context to demonstrate state changes
|
38
38
|
* and a transaction-based event payload for testing event correlation.
|
39
39
|
*/
|
40
|
-
describe("
|
40
|
+
describe("GraphFlow", function () {
|
41
41
|
let graph: GraphFlow<TestSchema>;
|
42
42
|
let eventEmitter: EventEmitter;
|
43
43
|
|
@@ -228,27 +228,32 @@ describe("Graph", function () {
|
|
228
228
|
* - Context preservation between attempts
|
229
229
|
* Essential for handling transient failures in workflows
|
230
230
|
*/
|
231
|
-
it("should retry a node execution when it fails", async
|
232
|
-
let
|
233
|
-
const
|
234
|
-
|
235
|
-
|
236
|
-
execute: async (
|
237
|
-
|
238
|
-
if (
|
231
|
+
it("should retry a node execution when it fails", async () => {
|
232
|
+
let attempts = 0;
|
233
|
+
const nodes = new Map();
|
234
|
+
nodes.set("test", {
|
235
|
+
name: "test",
|
236
|
+
execute: async () => {
|
237
|
+
attempts++;
|
238
|
+
if (attempts < 3) {
|
239
239
|
throw new Error("Temporary failure");
|
240
240
|
}
|
241
|
-
context.value = (context.value ?? 0) + 1;
|
242
241
|
},
|
243
|
-
|
244
|
-
|
242
|
+
retry: {
|
243
|
+
maxAttempts: 3,
|
244
|
+
delay: 100,
|
245
|
+
},
|
246
|
+
});
|
245
247
|
|
246
|
-
graph
|
247
|
-
|
248
|
+
const graph = new GraphFlow("test", {
|
249
|
+
name: "test",
|
250
|
+
schema: TestSchema,
|
251
|
+
context: { value: 0 },
|
252
|
+
nodes: Array.from(nodes.values()),
|
253
|
+
});
|
248
254
|
|
249
|
-
|
250
|
-
expect(
|
251
|
-
expect(attemptCount).to.equal(3);
|
255
|
+
await graph.execute("test");
|
256
|
+
expect(attempts).to.equal(3);
|
252
257
|
});
|
253
258
|
|
254
259
|
/**
|
@@ -294,27 +299,29 @@ describe("Graph", function () {
|
|
294
299
|
/**
|
295
300
|
* Tests input validation error handling
|
296
301
|
*/
|
297
|
-
it("should throw error when node input validation fails", async
|
298
|
-
const
|
299
|
-
|
300
|
-
|
301
|
-
amount: z.number().min(0),
|
302
|
-
}),
|
303
|
-
execute: async (context, inputs: { amount: number }) => {
|
304
|
-
context.value = (context.value ?? 0) + inputs.amount;
|
305
|
-
},
|
306
|
-
next: [],
|
307
|
-
};
|
302
|
+
it("should throw error when node input validation fails", async () => {
|
303
|
+
const InputSchema = z.object({
|
304
|
+
value: z.number().min(0),
|
305
|
+
});
|
308
306
|
|
309
|
-
graph
|
307
|
+
const graph = new GraphFlow("test", {
|
308
|
+
name: "test",
|
309
|
+
schema: TestSchema,
|
310
|
+
context: { value: 0 },
|
311
|
+
nodes: [
|
312
|
+
{
|
313
|
+
name: "test",
|
314
|
+
inputs: InputSchema,
|
315
|
+
execute: async () => {},
|
316
|
+
},
|
317
|
+
],
|
318
|
+
});
|
310
319
|
|
311
320
|
try {
|
312
|
-
await graph.execute("
|
321
|
+
await graph.execute("test", { value: -1 });
|
313
322
|
expect.fail("Should have thrown an error");
|
314
|
-
} catch (error) {
|
315
|
-
expect(
|
316
|
-
"Number must be greater than or equal to 0"
|
317
|
-
);
|
323
|
+
} catch (error: any) {
|
324
|
+
expect(error.message).to.include("Number must be greater than or equal");
|
318
325
|
}
|
319
326
|
});
|
320
327
|
|
@@ -0,0 +1,197 @@
|
|
1
|
+
import { expect, use } from "chai";
|
2
|
+
import chaiAsPromised from "chai-as-promised";
|
3
|
+
import { EventEmitter } from "events";
|
4
|
+
import { BehaviorSubject, Subject } from "rxjs";
|
5
|
+
import { z } from "zod";
|
6
|
+
import { GraphEventManager } from "../../graph/event-manager";
|
7
|
+
import { GraphLogger } from "../../graph/logger";
|
8
|
+
import { GraphNode } from "../../graph/node";
|
9
|
+
import { GraphContext } from "../../types";
|
10
|
+
|
11
|
+
use(chaiAsPromised);
|
12
|
+
|
13
|
+
describe("GraphNode", () => {
|
14
|
+
const TestSchema = z.object({
|
15
|
+
counter: z.number(),
|
16
|
+
message: z.string(),
|
17
|
+
});
|
18
|
+
|
19
|
+
type TestContext = GraphContext<typeof TestSchema>;
|
20
|
+
|
21
|
+
let node: GraphNode<typeof TestSchema>;
|
22
|
+
let eventManager: GraphEventManager<typeof TestSchema>;
|
23
|
+
let logger: GraphLogger;
|
24
|
+
let eventEmitter: EventEmitter;
|
25
|
+
let eventSubject: Subject<any>;
|
26
|
+
let stateSubject: BehaviorSubject<any>;
|
27
|
+
let events: any[] = [];
|
28
|
+
|
29
|
+
beforeEach(() => {
|
30
|
+
events = [];
|
31
|
+
eventEmitter = new EventEmitter();
|
32
|
+
eventSubject = new Subject();
|
33
|
+
stateSubject = new BehaviorSubject({ counter: 0, message: "Hello" });
|
34
|
+
logger = new GraphLogger("test", false);
|
35
|
+
eventManager = new GraphEventManager(eventEmitter, new Map(), "test", {
|
36
|
+
counter: 0,
|
37
|
+
message: "Hello",
|
38
|
+
});
|
39
|
+
|
40
|
+
// Capture des événements
|
41
|
+
eventSubject.subscribe((event) => events.push(event));
|
42
|
+
|
43
|
+
node = new GraphNode(
|
44
|
+
new Map(),
|
45
|
+
logger,
|
46
|
+
eventManager,
|
47
|
+
eventSubject,
|
48
|
+
stateSubject
|
49
|
+
);
|
50
|
+
});
|
51
|
+
|
52
|
+
it("should execute a simple node", async () => {
|
53
|
+
const nodes = new Map();
|
54
|
+
nodes.set("test", {
|
55
|
+
name: "test",
|
56
|
+
execute: async (context: TestContext) => {
|
57
|
+
context.counter++;
|
58
|
+
},
|
59
|
+
});
|
60
|
+
|
61
|
+
node = new GraphNode(
|
62
|
+
nodes,
|
63
|
+
logger,
|
64
|
+
eventManager,
|
65
|
+
eventSubject,
|
66
|
+
stateSubject
|
67
|
+
);
|
68
|
+
|
69
|
+
await node.executeNode("test", { counter: 0, message: "Hello" }, null);
|
70
|
+
|
71
|
+
// Vérifier les événements émis
|
72
|
+
expect(events).to.have.lengthOf(3); // nodeStarted, nodeStateChanged, nodeCompleted
|
73
|
+
expect(events[0].type).to.equal("nodeStarted");
|
74
|
+
expect(events[1].type).to.equal("nodeStateChanged");
|
75
|
+
expect(events[2].type).to.equal("nodeCompleted");
|
76
|
+
});
|
77
|
+
|
78
|
+
it("should handle node condition", async () => {
|
79
|
+
const nodes = new Map();
|
80
|
+
nodes.set("test", {
|
81
|
+
name: "test",
|
82
|
+
condition: (context: TestContext) => context.counter < 5,
|
83
|
+
execute: async (context: TestContext) => {
|
84
|
+
context.counter++;
|
85
|
+
},
|
86
|
+
});
|
87
|
+
|
88
|
+
node = new GraphNode(
|
89
|
+
nodes,
|
90
|
+
logger,
|
91
|
+
eventManager,
|
92
|
+
eventSubject,
|
93
|
+
stateSubject
|
94
|
+
);
|
95
|
+
|
96
|
+
// Test avec condition vraie
|
97
|
+
await node.executeNode("test", { counter: 0, message: "Hello" }, null);
|
98
|
+
expect(events.some((e) => e.type === "nodeStateChanged")).to.be.true;
|
99
|
+
|
100
|
+
// Test avec condition fausse
|
101
|
+
events = [];
|
102
|
+
await node.executeNode("test", { counter: 5, message: "Hello" }, null);
|
103
|
+
expect(events.some((e) => e.type === "nodeStateChanged")).to.be.false;
|
104
|
+
});
|
105
|
+
|
106
|
+
it("should handle errors", async () => {
|
107
|
+
const nodes = new Map();
|
108
|
+
nodes.set("test", {
|
109
|
+
name: "test",
|
110
|
+
execute: async (_context: TestContext) => {
|
111
|
+
throw new Error("Test error");
|
112
|
+
},
|
113
|
+
});
|
114
|
+
|
115
|
+
node = new GraphNode(
|
116
|
+
nodes,
|
117
|
+
logger,
|
118
|
+
eventManager,
|
119
|
+
eventSubject,
|
120
|
+
stateSubject
|
121
|
+
);
|
122
|
+
|
123
|
+
try {
|
124
|
+
await node.executeNode("test", { counter: 0, message: "Hello" }, null);
|
125
|
+
expect.fail("Should have thrown an error");
|
126
|
+
} catch (error: any) {
|
127
|
+
expect(error.message).to.equal("Test error");
|
128
|
+
const errorEvents = events.filter((e) => e.type === "nodeError");
|
129
|
+
expect(errorEvents).to.have.lengthOf(1);
|
130
|
+
expect(errorEvents[0].payload.error.message).to.equal("Test error");
|
131
|
+
}
|
132
|
+
});
|
133
|
+
|
134
|
+
it("should emit events exactly once per state change", async () => {
|
135
|
+
const nodes = new Map();
|
136
|
+
nodes.set("test", {
|
137
|
+
name: "test",
|
138
|
+
execute: async (context: TestContext) => {
|
139
|
+
context.counter++;
|
140
|
+
context.message = "Updated";
|
141
|
+
},
|
142
|
+
});
|
143
|
+
|
144
|
+
node = new GraphNode(
|
145
|
+
nodes,
|
146
|
+
logger,
|
147
|
+
eventManager,
|
148
|
+
eventSubject,
|
149
|
+
stateSubject
|
150
|
+
);
|
151
|
+
await node.executeNode("test", { counter: 0, message: "Hello" }, null);
|
152
|
+
|
153
|
+
// Compter les occurrences de chaque type d'événement
|
154
|
+
const eventCounts = events.reduce((acc, event) => {
|
155
|
+
acc[event.type] = (acc[event.type] || 0) + 1;
|
156
|
+
return acc;
|
157
|
+
}, {} as Record<string, number>);
|
158
|
+
|
159
|
+
expect(eventCounts).to.deep.equal({
|
160
|
+
nodeStarted: 1,
|
161
|
+
nodeStateChanged: 2, // Un pour counter, un pour message
|
162
|
+
nodeCompleted: 1,
|
163
|
+
});
|
164
|
+
|
165
|
+
// Vérifier l'ordre des événements
|
166
|
+
expect(events.map((e) => e.type)).to.deep.equal([
|
167
|
+
"nodeStarted",
|
168
|
+
"nodeStateChanged", // counter
|
169
|
+
"nodeStateChanged", // message
|
170
|
+
"nodeCompleted",
|
171
|
+
]);
|
172
|
+
});
|
173
|
+
|
174
|
+
it("should emit nodeStateChanged only for actual changes", async () => {
|
175
|
+
const nodes = new Map();
|
176
|
+
nodes.set("test", {
|
177
|
+
name: "test",
|
178
|
+
execute: async (context: TestContext) => {
|
179
|
+
context.counter = context.counter; // Même valeur
|
180
|
+
context.message = "New"; // Nouvelle valeur
|
181
|
+
},
|
182
|
+
});
|
183
|
+
|
184
|
+
node = new GraphNode(
|
185
|
+
nodes,
|
186
|
+
logger,
|
187
|
+
eventManager,
|
188
|
+
eventSubject,
|
189
|
+
stateSubject
|
190
|
+
);
|
191
|
+
await node.executeNode("test", { counter: 0, message: "Hello" }, null);
|
192
|
+
|
193
|
+
const stateChanges = events.filter((e) => e.type === "nodeStateChanged");
|
194
|
+
expect(stateChanges).to.have.lengthOf(1); // Seulement pour message
|
195
|
+
expect(stateChanges[0].payload.property).to.equal("message");
|
196
|
+
});
|
197
|
+
});
|
package/tsconfig.json
CHANGED
@@ -2,14 +2,25 @@
|
|
2
2
|
"compilerOptions": {
|
3
3
|
"target": "es2016",
|
4
4
|
"module": "commonjs",
|
5
|
+
"declaration": true,
|
6
|
+
"declarationMap": true,
|
7
|
+
"sourceMap": true,
|
5
8
|
"esModuleInterop": true,
|
6
9
|
"forceConsistentCasingInFileNames": true,
|
7
10
|
"strict": true,
|
8
11
|
"skipLibCheck": true,
|
9
12
|
"baseUrl": ".",
|
10
13
|
"outDir": "./dist",
|
11
|
-
"types": ["node", "mocha"]
|
14
|
+
"types": ["node", "mocha"],
|
15
|
+
"resolveJsonModule": true
|
12
16
|
},
|
13
|
-
"include": [
|
17
|
+
"include": [
|
18
|
+
"index.ts",
|
19
|
+
"modules/**/*",
|
20
|
+
"graph/**/*",
|
21
|
+
"types/**/*",
|
22
|
+
"interfaces/**/*",
|
23
|
+
"utils/**/*"
|
24
|
+
],
|
14
25
|
"exclude": ["node_modules", "dist", "test", "examples"]
|
15
26
|
}
|