@minded-ai/mindedjs 1.0.19
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/.github/workflows/CI.yml +34 -0
- package/.prettierrc +8 -0
- package/README.md +6 -0
- package/dist/agent.d.ts +36 -0
- package/dist/agent.js +199 -0
- package/dist/agent.js.map +1 -0
- package/dist/analytics.d.ts +6 -0
- package/dist/analytics.js +19 -0
- package/dist/analytics.js.map +1 -0
- package/dist/edges/createDirectEdge.d.ts +4 -0
- package/dist/edges/createDirectEdge.js +14 -0
- package/dist/edges/createDirectEdge.js.map +1 -0
- package/dist/edges/createLogicalRouter.d.ts +5 -0
- package/dist/edges/createLogicalRouter.js +18 -0
- package/dist/edges/createLogicalRouter.js.map +1 -0
- package/dist/edges/createPromptRouter.d.ts +7 -0
- package/dist/edges/createPromptRouter.js +54 -0
- package/dist/edges/createPromptRouter.js.map +1 -0
- package/dist/edges/edgeFactory.d.ts +9 -0
- package/dist/edges/edgeFactory.js +65 -0
- package/dist/edges/edgeFactory.js.map +1 -0
- package/dist/events/AgentEvents.d.ts +22 -0
- package/dist/events/AgentEvents.js +9 -0
- package/dist/events/AgentEvents.js.map +1 -0
- package/dist/events/index.d.ts +2 -0
- package/dist/events/index.js +5 -0
- package/dist/events/index.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure.ts/mindedRequest.d.ts +8 -0
- package/dist/infrastructure.ts/mindedRequest.js +22 -0
- package/dist/infrastructure.ts/mindedRequest.js.map +1 -0
- package/dist/llm/createLlmInstance.d.ts +2 -0
- package/dist/llm/createLlmInstance.js +14 -0
- package/dist/llm/createLlmInstance.js.map +1 -0
- package/dist/nodes/addHumanInTheLoopNode.d.ts +8 -0
- package/dist/nodes/addHumanInTheLoopNode.js +17 -0
- package/dist/nodes/addHumanInTheLoopNode.js.map +1 -0
- package/dist/nodes/addPromptNode.d.ts +15 -0
- package/dist/nodes/addPromptNode.js +52 -0
- package/dist/nodes/addPromptNode.js.map +1 -0
- package/dist/nodes/addToolNode.d.ts +10 -0
- package/dist/nodes/addToolNode.js +82 -0
- package/dist/nodes/addToolNode.js.map +1 -0
- package/dist/nodes/addTriggerNode.d.ts +6 -0
- package/dist/nodes/addTriggerNode.js +12 -0
- package/dist/nodes/addTriggerNode.js.map +1 -0
- package/dist/nodes/nodeFactory.d.ts +13 -0
- package/dist/nodes/nodeFactory.js +41 -0
- package/dist/nodes/nodeFactory.js.map +1 -0
- package/dist/platform/analytics.d.ts +6 -0
- package/dist/platform/analytics.js +19 -0
- package/dist/platform/analytics.js.map +1 -0
- package/dist/platform/mindedCheckpointSaver.d.ts +10 -0
- package/dist/platform/mindedCheckpointSaver.js +49 -0
- package/dist/platform/mindedCheckpointSaver.js.map +1 -0
- package/dist/platform/mindedConnection.d.ts +13 -0
- package/dist/platform/mindedConnection.js +117 -0
- package/dist/platform/mindedConnection.js.map +1 -0
- package/dist/platform/mindedConnectionTypes.d.ts +10 -0
- package/dist/platform/mindedConnectionTypes.js +8 -0
- package/dist/platform/mindedConnectionTypes.js.map +1 -0
- package/dist/platform/mindedRequest.d.ts +8 -0
- package/dist/platform/mindedRequest.js +22 -0
- package/dist/platform/mindedRequest.js.map +1 -0
- package/dist/types/Agent.types.d.ts +8 -0
- package/dist/types/Agent.types.js +3 -0
- package/dist/types/Agent.types.js.map +1 -0
- package/dist/types/Flows.types.d.ts +83 -0
- package/dist/types/Flows.types.js +24 -0
- package/dist/types/Flows.types.js.map +1 -0
- package/dist/types/LLM.types.d.ts +10 -0
- package/dist/types/LLM.types.js +9 -0
- package/dist/types/LLM.types.js.map +1 -0
- package/dist/types/LangGraph.types.d.ts +29 -0
- package/dist/types/LangGraph.types.js +20 -0
- package/dist/types/LangGraph.types.js.map +1 -0
- package/dist/types/Tools.types.d.ts +14 -0
- package/dist/types/Tools.types.js +3 -0
- package/dist/types/Tools.types.js.map +1 -0
- package/dist/types/Triggers.types.d.ts +1 -0
- package/dist/types/Triggers.types.js +3 -0
- package/dist/types/Triggers.types.js.map +1 -0
- package/docs/.gitbook/assets/image.png +0 -0
- package/docs/README.md +51 -0
- package/docs/SUMMARY.md +21 -0
- package/docs/api-reference/.nojekyll +1 -0
- package/docs/api-reference/assets/hierarchy.js +1 -0
- package/docs/api-reference/assets/highlight.css +120 -0
- package/docs/api-reference/assets/icons.js +18 -0
- package/docs/api-reference/assets/icons.svg +1 -0
- package/docs/api-reference/assets/main.js +60 -0
- package/docs/api-reference/assets/navigation.js +1 -0
- package/docs/api-reference/assets/search.js +1 -0
- package/docs/api-reference/assets/style.css +1640 -0
- package/docs/api-reference/classes/index.Agent.html +4 -0
- package/docs/api-reference/enums/index.EdgeType.html +4 -0
- package/docs/api-reference/enums/index.NodeType.html +6 -0
- package/docs/api-reference/enums/index.TriggerType.html +4 -0
- package/docs/api-reference/enums/index.events.html +3 -0
- package/docs/api-reference/hierarchy.html +1 -0
- package/docs/api-reference/index.html +310 -0
- package/docs/api-reference/interfaces/index.AppToolNode.html +5 -0
- package/docs/api-reference/interfaces/index.AppTriggerNode.html +6 -0
- package/docs/api-reference/interfaces/index.Flow.html +4 -0
- package/docs/api-reference/interfaces/index.JunctionNode.html +4 -0
- package/docs/api-reference/interfaces/index.LogicalConditionEdge.html +5 -0
- package/docs/api-reference/interfaces/index.ManualTriggerNode.html +5 -0
- package/docs/api-reference/interfaces/index.PromptConditionEdge.html +5 -0
- package/docs/api-reference/interfaces/index.PromptNode.html +6 -0
- package/docs/api-reference/interfaces/index.StepForwardEdge.html +4 -0
- package/docs/api-reference/interfaces/index.Tool.html +6 -0
- package/docs/api-reference/interfaces/index.ToolNode.html +5 -0
- package/docs/api-reference/modules/index-1.html +1 -0
- package/docs/api-reference/modules/index.html +1 -0
- package/docs/api-reference/modules.html +1 -0
- package/docs/api-reference/types/index.Edge.html +1 -0
- package/docs/api-reference/types/index.Node.html +1 -0
- package/docs/api-reference/types/index.TriggerNode.html +1 -0
- package/docs/core-concepts/edges.md +242 -0
- package/docs/core-concepts/events.md +161 -0
- package/docs/core-concepts/flows.md +74 -0
- package/docs/core-concepts/memory-types.md +208 -0
- package/docs/core-concepts/nodes.md +239 -0
- package/docs/core-concepts/tools.md +205 -0
- package/docs/examples/order-refund-flow.md +560 -0
- package/docs/getting-started/installation.md +34 -0
- package/docs/getting-started/quick-start.md +264 -0
- package/docs-structure.md +144 -0
- package/eslint.config.js +68 -0
- package/examples/orderRefundAgent/flows/orderRefundFlow.yaml +32 -0
- package/examples/orderRefundAgent/minded.json +14 -0
- package/examples/orderRefundAgent/orderRefundAgent.ts +58 -0
- package/examples/orderRefundAgent/schema.ts +7 -0
- package/examples/orderRefundAgent/tools/escalateConversation.ts +28 -0
- package/examples/orderRefundAgent/tools/index.ts +4 -0
- package/examples/orderRefundAgent/tools/refundOrder.ts +27 -0
- package/package.json +46 -0
- package/src/agent.ts +216 -0
- package/src/edges/createDirectEdge.ts +11 -0
- package/src/edges/createLogicalRouter.ts +16 -0
- package/src/edges/createPromptRouter.ts +52 -0
- package/src/edges/edgeFactory.ts +85 -0
- package/src/events/AgentEvents.ts +22 -0
- package/src/events/index.ts +3 -0
- package/src/index.ts +22 -0
- package/src/llm/createLlmInstance.ts +10 -0
- package/src/nodes/addHumanInTheLoopNode.ts +20 -0
- package/src/nodes/addPromptNode.ts +66 -0
- package/src/nodes/addToolNode.ts +95 -0
- package/src/nodes/addTriggerNode.ts +12 -0
- package/src/nodes/nodeFactory.ts +65 -0
- package/src/platform/analytics.ts +16 -0
- package/src/platform/mindedCheckpointSaver.ts +74 -0
- package/src/platform/mindedConnection.ts +106 -0
- package/src/platform/mindedConnectionTypes.ts +15 -0
- package/src/platform/mindedRequest.ts +28 -0
- package/src/types/Agent.types.ts +10 -0
- package/src/types/Flows.types.ts +103 -0
- package/src/types/LLM.types.ts +13 -0
- package/src/types/LangGraph.types.ts +25 -0
- package/src/types/Tools.types.ts +9 -0
- package/test/can-stay-on-node/can-stay-on-node.test.ts +148 -0
- package/test/can-stay-on-node/flows/test-flow.yaml +25 -0
- package/test/cannot-stay-on-node/cannot-stay-on-node.test.ts +201 -0
- package/test/cannot-stay-on-node/flows/test-flow.yaml +34 -0
- package/test/human-in-the-loop-node/flows/test-flow.yaml +23 -0
- package/test/human-in-the-loop-node/human-in-the-loop-node.test.ts +92 -0
- package/test/logical-edges/flows/logical-edge-test-flow.yaml +24 -0
- package/test/logical-edges/logical-edges.test.ts +66 -0
- package/test/no-human-in-the-loop-node/flows/test-flow.yaml +23 -0
- package/test/no-human-in-the-loop-node/no-human-in-the-loop-node.test.ts +80 -0
- package/test/prompt-edges/flows/test-flow.yaml +24 -0
- package/test/prompt-edges/prompt-edges.test.ts +90 -0
- package/test/prompt-node/flows/test-flow.yaml +24 -0
- package/test/prompt-node/prompt-node.test.ts +86 -0
- package/test/setup.ts +5 -0
- package/test/tool-node/flows/test-flow.yaml +14 -0
- package/test/tool-node/tool-node.test.ts +67 -0
- package/test/trigger/flows/test-flow.yaml +7 -0
- package/test/trigger/trigger.test.ts +57 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { Agent } from '../../src/agent';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { AgentEvents } from '../../src/events/AgentEvents';
|
|
7
|
+
import { HumanMessage } from '@langchain/core/messages';
|
|
8
|
+
|
|
9
|
+
const memorySchema = z.object({
|
|
10
|
+
success: z.boolean(),
|
|
11
|
+
alternative: z.boolean(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('Cannot Stay On Node', function () {
|
|
15
|
+
this.timeout(10000); // Set timeout to 10 seconds for all tests in this suite
|
|
16
|
+
|
|
17
|
+
let agent: Agent;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
const flowsPath = path.join(__dirname, 'flows');
|
|
21
|
+
const toolsPath = path.join(__dirname, 'tools');
|
|
22
|
+
agent = new Agent({
|
|
23
|
+
memorySchema,
|
|
24
|
+
config: {
|
|
25
|
+
flows: [flowsPath],
|
|
26
|
+
tools: [toolsPath],
|
|
27
|
+
llm: {
|
|
28
|
+
name: 'ChatOpenAI',
|
|
29
|
+
properties: {
|
|
30
|
+
model: 'gpt-4o-mini',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
tools: [
|
|
35
|
+
{
|
|
36
|
+
name: 'successTool',
|
|
37
|
+
description: 'Success Tool that marks completion',
|
|
38
|
+
input: z.object({
|
|
39
|
+
name: z.string(),
|
|
40
|
+
}),
|
|
41
|
+
execute: async () => ({
|
|
42
|
+
memory: {
|
|
43
|
+
success: true,
|
|
44
|
+
alternative: false,
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'alternativeTool',
|
|
50
|
+
description: 'Alternative Tool that marks alternative completion',
|
|
51
|
+
input: z.object({
|
|
52
|
+
name: z.string(),
|
|
53
|
+
}),
|
|
54
|
+
execute: async () => ({
|
|
55
|
+
memory: {
|
|
56
|
+
success: false,
|
|
57
|
+
alternative: true,
|
|
58
|
+
},
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Add trigger event handler
|
|
65
|
+
agent.on(AgentEvents.TRIGGER_EVENT, async ({ triggerName, triggerBody }) => {
|
|
66
|
+
if (triggerName === 'Trigger') {
|
|
67
|
+
return {
|
|
68
|
+
memory: {},
|
|
69
|
+
messages: [new HumanMessage(triggerBody.message)],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should NOT stay on node when explicitly requested to stay (canStayOnNode is false)', async function () {
|
|
76
|
+
this.timeout(20000);
|
|
77
|
+
|
|
78
|
+
const sessionId = uuidv4();
|
|
79
|
+
|
|
80
|
+
// First invocation - triggers the flow and reaches the human-in-the-loop pause
|
|
81
|
+
const result1 = await agent.invoke({
|
|
82
|
+
triggerBody: {
|
|
83
|
+
message: 'Initial input for processing',
|
|
84
|
+
},
|
|
85
|
+
triggerName: 'Trigger',
|
|
86
|
+
sessionId,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Should pause at human-in-the-loop, not reach any tool yet
|
|
90
|
+
expect(result1.memory.success).to.not.equal(true);
|
|
91
|
+
expect(result1.memory.alternative).to.not.equal(true);
|
|
92
|
+
expect(result1.messages).to.have.length.greaterThan(0);
|
|
93
|
+
|
|
94
|
+
// Second invocation - explicitly request to stay on the node
|
|
95
|
+
const result2 = await agent.invoke({
|
|
96
|
+
triggerBody: {
|
|
97
|
+
message: 'You must stay at the node! Do not move to the next step. Stay here and continue processing on this same node.',
|
|
98
|
+
},
|
|
99
|
+
triggerName: 'Trigger',
|
|
100
|
+
sessionId,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Should NOT stay on the node - should move to one of the target nodes
|
|
104
|
+
// Since canStayOnNode is false, the router has no self-referencing edge option
|
|
105
|
+
expect(result2.memory.success || result2.memory.alternative).to.equal(true);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should move to success node when user requests main workflow', async function () {
|
|
109
|
+
this.timeout(20000);
|
|
110
|
+
|
|
111
|
+
const sessionId = uuidv4();
|
|
112
|
+
|
|
113
|
+
// First invocation - initial trigger
|
|
114
|
+
const result1 = await agent.invoke({
|
|
115
|
+
triggerBody: {
|
|
116
|
+
message: 'Start processing',
|
|
117
|
+
},
|
|
118
|
+
triggerName: 'Trigger',
|
|
119
|
+
sessionId,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(result1.memory.success).to.not.equal(true);
|
|
123
|
+
expect(result1.memory.alternative).to.not.equal(true);
|
|
124
|
+
|
|
125
|
+
// Second invocation - request main workflow
|
|
126
|
+
const result2 = await agent.invoke({
|
|
127
|
+
triggerBody: {
|
|
128
|
+
message: 'Proceed with the main workflow',
|
|
129
|
+
},
|
|
130
|
+
triggerName: 'Trigger',
|
|
131
|
+
sessionId,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Should move to success node
|
|
135
|
+
expect(result2.memory.success).to.equal(true);
|
|
136
|
+
expect(result2.memory.alternative).to.equal(false);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should move to alternative node when user requests alternative approach', async function () {
|
|
140
|
+
this.timeout(20000);
|
|
141
|
+
|
|
142
|
+
const sessionId = uuidv4();
|
|
143
|
+
|
|
144
|
+
// First invocation - initial trigger
|
|
145
|
+
const result1 = await agent.invoke({
|
|
146
|
+
triggerBody: {
|
|
147
|
+
message: 'Begin workflow',
|
|
148
|
+
},
|
|
149
|
+
triggerName: 'Trigger',
|
|
150
|
+
sessionId,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
expect(result1.memory.success).to.not.equal(true);
|
|
154
|
+
expect(result1.memory.alternative).to.not.equal(true);
|
|
155
|
+
|
|
156
|
+
// Second invocation - request alternative approach
|
|
157
|
+
const result2 = await agent.invoke({
|
|
158
|
+
triggerBody: {
|
|
159
|
+
message: 'I want an alternative approach instead',
|
|
160
|
+
},
|
|
161
|
+
triggerName: 'Trigger',
|
|
162
|
+
sessionId,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Should move to alternative node
|
|
166
|
+
expect(result2.memory.success).to.equal(false);
|
|
167
|
+
expect(result2.memory.alternative).to.equal(true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should force progression even with strong stay requests', async function () {
|
|
171
|
+
this.timeout(20000);
|
|
172
|
+
|
|
173
|
+
const sessionId = uuidv4();
|
|
174
|
+
|
|
175
|
+
// First invocation - initial trigger
|
|
176
|
+
const result1 = await agent.invoke({
|
|
177
|
+
triggerBody: {
|
|
178
|
+
message: 'Process this data',
|
|
179
|
+
},
|
|
180
|
+
triggerName: 'Trigger',
|
|
181
|
+
sessionId,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
expect(result1.memory.success).to.not.equal(true);
|
|
185
|
+
expect(result1.memory.alternative).to.not.equal(true);
|
|
186
|
+
|
|
187
|
+
// Second invocation - very strong request to stay
|
|
188
|
+
const result2 = await agent.invoke({
|
|
189
|
+
triggerBody: {
|
|
190
|
+
message:
|
|
191
|
+
'STAY! Do not proceed! You must remain on this current node and not move forward. Keep processing here. Do not go to any other node.',
|
|
192
|
+
},
|
|
193
|
+
triggerName: 'Trigger',
|
|
194
|
+
sessionId,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Should still be forced to move to one of the available target nodes
|
|
198
|
+
// because there's no self-referencing edge (canStayOnNode is false)
|
|
199
|
+
expect(result2.memory.success || result2.memory.alternative).to.equal(true);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Cannot Stay On Node Test Flow
|
|
2
|
+
nodes:
|
|
3
|
+
- type: 'trigger'
|
|
4
|
+
triggerType: 'manual'
|
|
5
|
+
name: 'Trigger'
|
|
6
|
+
|
|
7
|
+
- type: 'promptNode'
|
|
8
|
+
name: 'ProcessingNode'
|
|
9
|
+
prompt: 'I have processed your input. Please provide your feedback or additional instructions.'
|
|
10
|
+
humanInTheLoop: true
|
|
11
|
+
# Note: canStayOnNode is NOT set to true
|
|
12
|
+
|
|
13
|
+
- type: 'tool'
|
|
14
|
+
name: 'Success'
|
|
15
|
+
toolName: 'successTool'
|
|
16
|
+
|
|
17
|
+
- type: 'tool'
|
|
18
|
+
name: 'Alternative'
|
|
19
|
+
toolName: 'alternativeTool'
|
|
20
|
+
|
|
21
|
+
edges:
|
|
22
|
+
- source: 'Trigger'
|
|
23
|
+
target: 'ProcessingNode'
|
|
24
|
+
type: 'stepForward'
|
|
25
|
+
|
|
26
|
+
- source: 'ProcessingNode'
|
|
27
|
+
target: 'Success'
|
|
28
|
+
type: 'promptCondition'
|
|
29
|
+
prompt: 'move to success node if the user wants to proceed with the main workflow'
|
|
30
|
+
|
|
31
|
+
- source: 'ProcessingNode'
|
|
32
|
+
target: 'Alternative'
|
|
33
|
+
type: 'promptCondition'
|
|
34
|
+
prompt: 'move to alternative node if the user wants an alternative approach'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Human In The Loop Test Flow
|
|
2
|
+
nodes:
|
|
3
|
+
- type: 'trigger'
|
|
4
|
+
triggerType: 'manual'
|
|
5
|
+
name: 'Trigger'
|
|
6
|
+
|
|
7
|
+
- type: 'promptNode'
|
|
8
|
+
name: 'ProcessingNode'
|
|
9
|
+
prompt: 'Process the user input and prepare for human review'
|
|
10
|
+
humanInTheLoop: true
|
|
11
|
+
|
|
12
|
+
- type: 'tool'
|
|
13
|
+
name: 'Success'
|
|
14
|
+
toolName: 'successTool'
|
|
15
|
+
|
|
16
|
+
edges:
|
|
17
|
+
- source: 'Trigger'
|
|
18
|
+
target: 'ProcessingNode'
|
|
19
|
+
type: 'stepForward'
|
|
20
|
+
|
|
21
|
+
- source: 'ProcessingNode'
|
|
22
|
+
target: 'Success'
|
|
23
|
+
type: 'stepForward'
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { Agent } from '../../src/agent';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { AgentEvents } from '../../src/events/AgentEvents';
|
|
7
|
+
import { HumanMessage } from '@langchain/core/messages';
|
|
8
|
+
|
|
9
|
+
const memorySchema = z.object({
|
|
10
|
+
success: z.boolean(),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('Human In The Loop Node', function () {
|
|
14
|
+
this.timeout(10000); // Set timeout to 10 seconds for all tests in this suite
|
|
15
|
+
|
|
16
|
+
let agent: Agent;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
const flowsPath = path.join(__dirname, 'flows');
|
|
20
|
+
const toolsPath = path.join(__dirname, 'tools');
|
|
21
|
+
agent = new Agent({
|
|
22
|
+
memorySchema,
|
|
23
|
+
config: {
|
|
24
|
+
flows: [flowsPath],
|
|
25
|
+
tools: [toolsPath],
|
|
26
|
+
llm: {
|
|
27
|
+
name: 'ChatOpenAI',
|
|
28
|
+
properties: {
|
|
29
|
+
model: 'gpt-4.1',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
tools: [
|
|
34
|
+
{
|
|
35
|
+
name: 'successTool',
|
|
36
|
+
description: 'Success Tool',
|
|
37
|
+
input: z.object({
|
|
38
|
+
name: z.string(),
|
|
39
|
+
}),
|
|
40
|
+
execute: async () => ({
|
|
41
|
+
memory: {
|
|
42
|
+
success: true,
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Add trigger event handler
|
|
50
|
+
agent.on(AgentEvents.TRIGGER_EVENT, async ({ triggerName, triggerBody }) => {
|
|
51
|
+
if (triggerName === 'Trigger') {
|
|
52
|
+
return {
|
|
53
|
+
memory: {},
|
|
54
|
+
messages: [new HumanMessage(triggerBody.message)],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should pause execution at human-in-the-loop node and resume after human input', async function () {
|
|
61
|
+
this.timeout(20000); // Set timeout to 20 seconds for this test
|
|
62
|
+
|
|
63
|
+
const sessionId = uuidv4();
|
|
64
|
+
|
|
65
|
+
// First invocation should execute the ProcessingNode and then pause at the human-in-the-loop node
|
|
66
|
+
const result = await agent.invoke({
|
|
67
|
+
triggerBody: {
|
|
68
|
+
message: 'Initial user input for processing',
|
|
69
|
+
},
|
|
70
|
+
triggerName: 'Trigger',
|
|
71
|
+
sessionId,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Should pause execution at human-in-the-loop node, not reaching the success tool yet
|
|
75
|
+
expect(result.memory.success).to.not.equal(true);
|
|
76
|
+
|
|
77
|
+
// Verify that we have messages from the processing node
|
|
78
|
+
expect(result.messages).to.have.length.greaterThan(0);
|
|
79
|
+
|
|
80
|
+
// Second invocation should provide human input and continue to the Success node
|
|
81
|
+
const result2 = await agent.invoke({
|
|
82
|
+
triggerBody: {
|
|
83
|
+
message: 'Human approval: proceed with the workflow',
|
|
84
|
+
},
|
|
85
|
+
triggerName: 'Trigger',
|
|
86
|
+
sessionId,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Should complete execution and reach the success tool
|
|
90
|
+
expect(result2.memory.success).to.equal(true);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Age Flow
|
|
2
|
+
nodes:
|
|
3
|
+
- type: "trigger"
|
|
4
|
+
triggerType: "manual"
|
|
5
|
+
name: "AgeTrigger"
|
|
6
|
+
|
|
7
|
+
- type: "tool"
|
|
8
|
+
name: "Adult Tool"
|
|
9
|
+
toolName: "adultTool"
|
|
10
|
+
|
|
11
|
+
- type: "tool"
|
|
12
|
+
name: "Minor Tool"
|
|
13
|
+
toolName: "minorTool"
|
|
14
|
+
|
|
15
|
+
edges:
|
|
16
|
+
- source: "AgeTrigger"
|
|
17
|
+
target: "Adult Tool"
|
|
18
|
+
type: "logicalCondition"
|
|
19
|
+
condition: "({ memory }) => memory.userAge >= 18"
|
|
20
|
+
|
|
21
|
+
- source: "AgeTrigger"
|
|
22
|
+
target: "Minor Tool"
|
|
23
|
+
type: "logicalCondition"
|
|
24
|
+
condition: "({ memory }) => memory.userAge < 18"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { Agent } from "../../src/agent";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { AgentEvents } from "../../src/events/AgentEvents";
|
|
6
|
+
|
|
7
|
+
const memorySchema = z.object({
|
|
8
|
+
userAge: z.number(),
|
|
9
|
+
isMinor: z.boolean(),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('Logical Edges', () => {
|
|
13
|
+
const flowsPath = path.join(__dirname, 'flows');
|
|
14
|
+
const toolsPath = path.join(__dirname, 'tools');
|
|
15
|
+
const agent = new Agent({
|
|
16
|
+
memorySchema,
|
|
17
|
+
config: {
|
|
18
|
+
flows: [flowsPath],
|
|
19
|
+
tools: [toolsPath],
|
|
20
|
+
llm: {
|
|
21
|
+
name: 'ChatOpenAI',
|
|
22
|
+
properties: {
|
|
23
|
+
model: 'gpt-4.1'
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
tools: [
|
|
28
|
+
{
|
|
29
|
+
name: 'adultTool',
|
|
30
|
+
description: 'Adult Tool',
|
|
31
|
+
input: z.object({}),
|
|
32
|
+
execute: async () => ({ memory: { isMinor: false } }),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'minorTool',
|
|
36
|
+
description: 'Minor Tool',
|
|
37
|
+
input: z.object({}),
|
|
38
|
+
execute: async () => ({ memory: { isMinor: true } }),
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
agent.on(AgentEvents.TRIGGER_EVENT, async ({ triggerBody }) => {
|
|
44
|
+
return {
|
|
45
|
+
memory: triggerBody
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should execute the Adult Tool', async function () {
|
|
50
|
+
this.timeout(10000); // Increase timeout to 10 seconds
|
|
51
|
+
const result = await agent.invoke({
|
|
52
|
+
triggerName: 'AgeTrigger',
|
|
53
|
+
triggerBody: { userAge: 25 },
|
|
54
|
+
});
|
|
55
|
+
expect(result.memory.isMinor).to.equal(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should execute the Minor Tool', async function () {
|
|
59
|
+
this.timeout(10000); // Increase timeout to 10 seconds
|
|
60
|
+
const result = await agent.invoke({
|
|
61
|
+
triggerName: 'AgeTrigger',
|
|
62
|
+
triggerBody: { userAge: 15 },
|
|
63
|
+
});
|
|
64
|
+
expect(result.memory.isMinor).to.equal(true);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: No Human In The Loop Test Flow
|
|
2
|
+
nodes:
|
|
3
|
+
- type: 'trigger'
|
|
4
|
+
triggerType: 'manual'
|
|
5
|
+
name: 'Trigger'
|
|
6
|
+
|
|
7
|
+
- type: 'promptNode'
|
|
8
|
+
name: 'ProcessingNode'
|
|
9
|
+
prompt: 'Process the user input and prepare for completion'
|
|
10
|
+
humanInTheLoop: false
|
|
11
|
+
|
|
12
|
+
- type: 'tool'
|
|
13
|
+
name: 'Success'
|
|
14
|
+
toolName: 'successTool'
|
|
15
|
+
|
|
16
|
+
edges:
|
|
17
|
+
- source: 'Trigger'
|
|
18
|
+
target: 'ProcessingNode'
|
|
19
|
+
type: 'stepForward'
|
|
20
|
+
|
|
21
|
+
- source: 'ProcessingNode'
|
|
22
|
+
target: 'Success'
|
|
23
|
+
type: 'stepForward'
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { Agent } from '../../src/agent';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { AgentEvents } from '../../src/events/AgentEvents';
|
|
7
|
+
import { HumanMessage } from '@langchain/core/messages';
|
|
8
|
+
|
|
9
|
+
const memorySchema = z.object({
|
|
10
|
+
success: z.boolean(),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('No Human In The Loop Node', function () {
|
|
14
|
+
this.timeout(10000); // Set timeout to 10 seconds for all tests in this suite
|
|
15
|
+
|
|
16
|
+
let agent: Agent;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
const flowsPath = path.join(__dirname, 'flows');
|
|
20
|
+
const toolsPath = path.join(__dirname, 'tools');
|
|
21
|
+
agent = new Agent({
|
|
22
|
+
memorySchema,
|
|
23
|
+
config: {
|
|
24
|
+
flows: [flowsPath],
|
|
25
|
+
tools: [toolsPath],
|
|
26
|
+
llm: {
|
|
27
|
+
name: 'ChatOpenAI',
|
|
28
|
+
properties: {
|
|
29
|
+
model: 'gpt-4.1',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
tools: [
|
|
34
|
+
{
|
|
35
|
+
name: 'successTool',
|
|
36
|
+
description: 'Success Tool',
|
|
37
|
+
input: z.object({
|
|
38
|
+
name: z.string(),
|
|
39
|
+
}),
|
|
40
|
+
execute: async () => ({
|
|
41
|
+
memory: {
|
|
42
|
+
success: true,
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Add trigger event handler
|
|
50
|
+
agent.on(AgentEvents.TRIGGER_EVENT, async ({ triggerName, triggerBody }) => {
|
|
51
|
+
if (triggerName === 'Trigger') {
|
|
52
|
+
return {
|
|
53
|
+
memory: {},
|
|
54
|
+
messages: [new HumanMessage(triggerBody.message)],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should complete execution without pausing when humanInTheLoop is false', async function () {
|
|
61
|
+
this.timeout(20000); // Set timeout to 20 seconds for this test
|
|
62
|
+
|
|
63
|
+
const sessionId = uuidv4();
|
|
64
|
+
|
|
65
|
+
// Single invocation should complete the entire flow without pausing
|
|
66
|
+
const result = await agent.invoke({
|
|
67
|
+
triggerBody: {
|
|
68
|
+
message: 'Input for processing without human intervention',
|
|
69
|
+
},
|
|
70
|
+
triggerName: 'Trigger',
|
|
71
|
+
sessionId,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Should complete execution and reach the success tool in one go
|
|
75
|
+
expect(result.memory.success).to.equal(true);
|
|
76
|
+
|
|
77
|
+
// Verify that we have messages from both the processing node and the tool execution
|
|
78
|
+
expect(result.messages).to.have.length.greaterThan(0);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Test Flow
|
|
2
|
+
nodes:
|
|
3
|
+
- type: 'trigger'
|
|
4
|
+
triggerType: 'manual'
|
|
5
|
+
name: 'Trigger'
|
|
6
|
+
|
|
7
|
+
- type: 'tool'
|
|
8
|
+
name: 'Success Tool'
|
|
9
|
+
toolName: 'successTool'
|
|
10
|
+
|
|
11
|
+
- type: 'tool'
|
|
12
|
+
name: 'Failure Tool'
|
|
13
|
+
toolName: 'failureTool'
|
|
14
|
+
|
|
15
|
+
edges:
|
|
16
|
+
- source: 'Trigger'
|
|
17
|
+
target: 'Success Tool'
|
|
18
|
+
type: 'promptCondition'
|
|
19
|
+
prompt: 'User name is Itay'
|
|
20
|
+
|
|
21
|
+
- source: 'Trigger'
|
|
22
|
+
target: 'Failure Tool'
|
|
23
|
+
type: 'promptCondition'
|
|
24
|
+
prompt: 'User name is not Itay'
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { Agent } from '../../src/agent';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { AgentEvents } from '../../src/events/AgentEvents';
|
|
6
|
+
import { HumanMessage } from '@langchain/core/messages';
|
|
7
|
+
|
|
8
|
+
const memorySchema = z.object({
|
|
9
|
+
userName: z.string(),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('Prompt Edges', () => {
|
|
13
|
+
let agent: Agent;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
const flowsPath = path.join(__dirname, 'flows');
|
|
17
|
+
const toolsPath = path.join(__dirname, 'tools');
|
|
18
|
+
agent = new Agent({
|
|
19
|
+
memorySchema,
|
|
20
|
+
config: {
|
|
21
|
+
flows: [flowsPath],
|
|
22
|
+
tools: [toolsPath],
|
|
23
|
+
llm: {
|
|
24
|
+
name: 'ChatOpenAI',
|
|
25
|
+
properties: {
|
|
26
|
+
model: 'gpt-4.1'
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
tools: [
|
|
31
|
+
{
|
|
32
|
+
name: 'successTool',
|
|
33
|
+
description: 'Success Tool',
|
|
34
|
+
input: z.object({
|
|
35
|
+
userName: z.string(),
|
|
36
|
+
}),
|
|
37
|
+
execute: async ({ input }) => ({
|
|
38
|
+
memory: { userName: input.userName },
|
|
39
|
+
}),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'failureTool',
|
|
43
|
+
description: 'Failure Tool',
|
|
44
|
+
input: z.object({
|
|
45
|
+
userName: z.string(),
|
|
46
|
+
}),
|
|
47
|
+
execute: async ({ input }) => ({
|
|
48
|
+
memory: { userName: input.userName },
|
|
49
|
+
}),
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Add trigger event handler
|
|
55
|
+
agent.on(AgentEvents.TRIGGER_EVENT, async ({ triggerName, triggerBody }) => {
|
|
56
|
+
if (triggerName === 'Trigger') {
|
|
57
|
+
return {
|
|
58
|
+
memory: {},
|
|
59
|
+
messages: [new HumanMessage(triggerBody.message)],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should evaluate prompt condition edge correctly', async function () {
|
|
66
|
+
this.timeout(10000); // Increase timeout to 10 seconds
|
|
67
|
+
|
|
68
|
+
const result = await agent.invoke({
|
|
69
|
+
triggerBody: {
|
|
70
|
+
message: 'Hi, my name is Itay',
|
|
71
|
+
},
|
|
72
|
+
triggerName: 'Trigger',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(result.memory.userName).to.equal('Itay');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should evaluate prompt condition edge correctly', async function () {
|
|
79
|
+
this.timeout(10000); // Increase timeout to 10 seconds
|
|
80
|
+
|
|
81
|
+
const result = await agent.invoke({
|
|
82
|
+
triggerBody: {
|
|
83
|
+
message: 'Hi, my name is Ilan',
|
|
84
|
+
},
|
|
85
|
+
triggerName: 'Trigger',
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(result.memory.userName).to.equal('Ilan');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Test Flow
|
|
2
|
+
nodes:
|
|
3
|
+
- type: 'trigger'
|
|
4
|
+
triggerType: 'manual'
|
|
5
|
+
name: 'Trigger'
|
|
6
|
+
|
|
7
|
+
- type: 'promptNode'
|
|
8
|
+
name: 'Prompt'
|
|
9
|
+
prompt: 'Ask the user for their name'
|
|
10
|
+
humanInTheLoop: true
|
|
11
|
+
canStayOnNode: true
|
|
12
|
+
|
|
13
|
+
- type: 'tool'
|
|
14
|
+
name: 'Success'
|
|
15
|
+
toolName: 'successTool'
|
|
16
|
+
|
|
17
|
+
edges:
|
|
18
|
+
- source: 'Trigger'
|
|
19
|
+
target: 'Prompt'
|
|
20
|
+
type: 'stepForward'
|
|
21
|
+
|
|
22
|
+
- source: 'Prompt'
|
|
23
|
+
target: 'Success'
|
|
24
|
+
type: 'stepForward'
|